Compare commits
62
Commits
main
...
d7dad3021f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7dad3021f | ||
|
|
a422cc70f0 | ||
|
|
f986cbb1e9 | ||
|
|
3d2688c8be | ||
|
|
a387ea124a | ||
|
|
02fcad6a8c | ||
|
|
890c42e62f | ||
|
|
d095ddec03 | ||
|
|
fa7ee4d87f | ||
|
|
000af8bfb1 | ||
|
|
950db9eb8c | ||
|
|
53756caea3 | ||
|
|
87ceb8c87f | ||
|
|
953dfaaad0 | ||
|
|
46fb8e2cc3 | ||
|
|
8b8ba4b618 | ||
|
|
209f2ed7e0 | ||
|
|
9d16dcebd5 | ||
|
|
7e9f571df1 | ||
|
|
0fa40fc63d | ||
|
|
5d6af050ad | ||
|
|
c1df6938b5 | ||
|
|
450503afc3 | ||
|
|
85cd8aee00 | ||
|
|
ba84f9d778 | ||
|
|
c256ce36e5 | ||
|
|
1ba93cce8a | ||
|
|
0b2fb65af3 | ||
|
|
181ad46c29 | ||
|
|
d20af42053 | ||
|
|
ba3d3b2d80 | ||
|
|
1b7c41ea07 | ||
|
|
810589647b | ||
|
|
7f0d3fbb6d | ||
|
|
c6844fcc47 | ||
|
|
36b99968b6 | ||
|
|
aa16fc29d3 | ||
|
|
0c684d4831 | ||
|
|
2eb050294b | ||
|
|
93d34306bc | ||
|
|
625084cfa5 | ||
|
|
c81748da2c | ||
|
|
f48618f94b | ||
|
|
5cb893af62 | ||
|
|
c32582a1cd | ||
|
|
05d7947a29 | ||
|
|
8c8d632557 | ||
|
|
9ea2899ca7 | ||
|
|
e2076cb044 | ||
|
|
b5a6a61e2d | ||
|
|
71db966697 | ||
|
|
5d41473f47 | ||
|
|
35df7afe88 | ||
|
|
512757ca4a | ||
|
|
0003734bed | ||
|
|
694bfeabf3 | ||
|
|
fa3915bc5d | ||
|
|
998b3d3072 | ||
|
|
66fe3e4fea | ||
|
|
5298c6870b | ||
|
|
67a8a06268 | ||
|
|
51c4853d51 |
@@ -0,0 +1,17 @@
|
||||
name: Bug report
|
||||
description: Report a reproducible client defect.
|
||||
title: 'bug: '
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction and expected behavior
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Client version or commit
|
||||
validations:
|
||||
required: true
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Feature request
|
||||
description: Propose a client-side change.
|
||||
title: 'feature: '
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: textarea
|
||||
id: behavior
|
||||
attributes:
|
||||
label: Desired behavior and affected states
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: plan
|
||||
attributes:
|
||||
label: Planning
|
||||
options:
|
||||
- label: I checked PLAN.md and this does not silently change an existing decision.
|
||||
required: true
|
||||
@@ -9,4 +9,12 @@ jobs:
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- run: test -f README.md && test -f AGENTS.md && test -f .prettierrc.json
|
||||
- run: npm ci
|
||||
- run: npm run format:check
|
||||
- run: npm run check
|
||||
- run: npm run build
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Client release check
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: ['v*.*.*']
|
||||
|
||||
jobs:
|
||||
release-check:
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- run: test -s CHANGELOG.md && npm ci && npm run format:check && npm run check && npm run build
|
||||
@@ -0,0 +1,16 @@
|
||||
name: Client dependency and security checks
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Audit JavaScript dependencies when a lockfile exists
|
||||
run: |
|
||||
if test -f package-lock.json; then
|
||||
npm audit --audit-level=high
|
||||
fi
|
||||
@@ -7,4 +7,3 @@ Read the parent `AGENTS.md`, `PLAN.md`, `TODO.md`, and `docs/wiki/README.md` bef
|
||||
- Use Prettier for Svelte, JavaScript/TypeScript, JSON, YAML, and Markdown.
|
||||
- Never store passwords, tokens, or secrets in source, settings, fixtures, logs, or artifacts.
|
||||
- Do not edit the parent `PLAN.md` without explicit user permission.
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
Notable client changes are recorded here using the Keep a Changelog format.
|
||||
Stable release entries require explicit user authorization.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Add administrator backup inventory and manual backup controls.
|
||||
@@ -1,4 +1,3 @@
|
||||
# Contributing
|
||||
|
||||
Run formatting, linting, type checks, tests, and the AppImage build before submission. Use deterministic mocked server/API fixtures.
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 ReaperOfVeriod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,9 +2,19 @@
|
||||
|
||||
This directory is the home of the standalone Linux client repository.
|
||||
|
||||
The current development shell connects to the server readiness endpoint and
|
||||
provides an administrator login gate. The browser/dev client keeps its bearer
|
||||
session in memory only; the native AppImage integration must replace that
|
||||
temporary behavior with the Linux kernel keyring before persistent client
|
||||
authentication is considered complete.
|
||||
|
||||
The client is a Svelte-based control surface distributed as an x86-64 AppImage. It communicates with `trixie-server` through the documented versioned HTTP and authenticated WebSocket interfaces.
|
||||
|
||||
Connection recovery follows the server policy: the first retry is immediate,
|
||||
then retries wait 3, 10, and 15 seconds, followed by an indefinite 30-second
|
||||
interval until the server reconnects. Manual connection checks do not create
|
||||
overlapping retry loops.
|
||||
|
||||
Implementation has not started. See the parent workspace's `PLAN.md`, `TODO.md`, and `AGENTS.md` before beginning work.
|
||||
|
||||
Consumes the versioned contracts from the sibling `trixie-api` repository.
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# Security
|
||||
|
||||
Report vulnerabilities privately through the hosting instance. Never include credentials, tokens, or real user data in issues or fixtures.
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Trixie</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+1554
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "trixy-linux-client",
|
||||
"private": true,
|
||||
"version": "0.1.0-dev",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/svelte": "^5.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"prettier": "^3.5.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"typescript": "^5.7.0",
|
||||
"vite": "^6.0.0"
|
||||
}
|
||||
}
|
||||
+1217
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
import './styles.css';
|
||||
import App from './App.svelte';
|
||||
import { mount } from 'svelte';
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!,
|
||||
});
|
||||
|
||||
export default app;
|
||||
+963
@@ -0,0 +1,963 @@
|
||||
@font-face {
|
||||
font-family: 'Rajdhani';
|
||||
src: url('/fonts/Rajdhani-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Rajdhani';
|
||||
src: url('/fonts/Rajdhani-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Rajdhani';
|
||||
src: url('/fonts/Rajdhani-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: url('/fonts/Inter-Variable.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
src: url('/fonts/JetBrainsMono-Variable.ttf') format('truetype');
|
||||
}
|
||||
:root {
|
||||
--ed-accent: #00b3ff;
|
||||
--ed-accent2: #ff7f00;
|
||||
--ed-bg: #06080c;
|
||||
--ed-panel: #0d1117;
|
||||
--ed-edge: #1d2630;
|
||||
--ed-fg: #dde6ec;
|
||||
--ed-dim: #7d8b96;
|
||||
--ed-success: #35d073;
|
||||
--ed-warn: #ffb400;
|
||||
--ed-danger: #ff5436;
|
||||
--ed-info: #00b3ff;
|
||||
--ed-exo: #a3ff5e;
|
||||
--font-display: 'Rajdhani', 'Inter', sans-serif;
|
||||
--font-data: 'JetBrains Mono', monospace;
|
||||
--font-body: 'Inter', sans-serif;
|
||||
--panel-radius: 2px;
|
||||
--ui-line: color-mix(in srgb, var(--ed-accent) 28%, var(--ed-edge));
|
||||
--ui-line-soft: color-mix(in srgb, var(--ed-accent) 16%, var(--ed-edge));
|
||||
--ui-line-focus: color-mix(in srgb, var(--ed-accent) 62%, var(--ed-edge));
|
||||
--ui-surface: color-mix(in srgb, var(--ed-accent) 8%, var(--ed-bg));
|
||||
--ui-surface-hi: color-mix(in srgb, var(--ed-accent) 14%, var(--ed-bg));
|
||||
--ui-divider: color-mix(in srgb, var(--ed-fg) 7%, transparent);
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
background: var(--ed-bg);
|
||||
color: var(--ed-fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
}
|
||||
button,
|
||||
input {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
border: 1px solid var(--ui-line);
|
||||
border-radius: var(--panel-radius);
|
||||
outline: none;
|
||||
background: var(--ed-bg);
|
||||
color: var(--ed-fg);
|
||||
font-family: var(--font-data);
|
||||
font-size: 11px;
|
||||
padding: 4px 6px;
|
||||
color-scheme: dark;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
select:focus-visible,
|
||||
textarea:focus-visible {
|
||||
border-color: var(--ui-line-focus);
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
border-color: var(--ui-line-focus);
|
||||
background: var(--ui-surface-hi);
|
||||
}
|
||||
.app-shell {
|
||||
min-height: 100vh;
|
||||
background: var(--ed-panel);
|
||||
}
|
||||
.app-bar {
|
||||
height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
background: var(--ed-bg);
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
min-width: 188px;
|
||||
color: var(--ed-accent);
|
||||
font: 600 15px var(--font-data);
|
||||
letter-spacing: 0.22em;
|
||||
}
|
||||
.brand-mark {
|
||||
color: var(--ed-accent);
|
||||
font-size: 16px;
|
||||
}
|
||||
.top-nav {
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
gap: 24px;
|
||||
}
|
||||
.nav-item {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: var(--ed-dim);
|
||||
background: transparent;
|
||||
font: 10px var(--font-data);
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.nav-item:hover,
|
||||
.nav-item.active {
|
||||
color: var(--ed-fg);
|
||||
background: transparent;
|
||||
}
|
||||
.nav-item.active::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
background: var(--ed-accent);
|
||||
content: '';
|
||||
}
|
||||
.app-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.connection-state,
|
||||
.badge {
|
||||
padding: 3px 7px;
|
||||
border: 1px solid var(--ed-warn);
|
||||
color: var(--ed-warn);
|
||||
font: 10px var(--font-data);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.connection-state.online {
|
||||
border-color: var(--ed-success);
|
||||
color: var(--ed-success);
|
||||
}
|
||||
.connection-notice {
|
||||
color: var(--ed-dim);
|
||||
font: 9px var(--font-data);
|
||||
}
|
||||
.icon-button,
|
||||
.secondary {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
color: var(--ed-dim);
|
||||
font-size: 18px;
|
||||
}
|
||||
.notification-wrap {
|
||||
position: relative;
|
||||
}
|
||||
.notification-button {
|
||||
position: relative;
|
||||
}
|
||||
.notification-dot {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--ed-warn);
|
||||
}
|
||||
.notification-popover {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
top: 34px;
|
||||
right: 0;
|
||||
width: 320px;
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--ui-line);
|
||||
background: var(--ed-panel);
|
||||
box-shadow: 0 8px 24px rgb(0 0 0 / 35%);
|
||||
}
|
||||
.notification-heading,
|
||||
.notification-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.notification-heading {
|
||||
padding: 9px 10px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
color: var(--ed-fg);
|
||||
font: 600 11px var(--font-data);
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
.notification-item {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
opacity: 0.65;
|
||||
}
|
||||
.notification-item.unseen {
|
||||
opacity: 1;
|
||||
background: var(--ui-surface);
|
||||
}
|
||||
.notification-title {
|
||||
color: var(--ed-fg);
|
||||
font-weight: 600;
|
||||
}
|
||||
.notification-message {
|
||||
margin: 6px 0 9px;
|
||||
color: var(--ed-dim);
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.notification-empty {
|
||||
padding: 18px 10px;
|
||||
color: var(--ed-dim);
|
||||
font: 11px var(--font-data);
|
||||
}
|
||||
.chat-message-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
.chat-message {
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
font-size: 11px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.chat-user {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: var(--ed-accent);
|
||||
font-family: var(--font-data);
|
||||
font-weight: 600;
|
||||
}
|
||||
.chat-text {
|
||||
color: var(--ed-fg);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.chat-emote {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 0 2px;
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
}
|
||||
.chat-reply {
|
||||
margin-left: auto;
|
||||
padding: 2px 4px;
|
||||
border-color: transparent;
|
||||
color: var(--ed-dim);
|
||||
font: 9px var(--font-data);
|
||||
}
|
||||
.chat-actions {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
gap: 3px;
|
||||
opacity: 0;
|
||||
}
|
||||
.chat-message:hover .chat-actions,
|
||||
.chat-message:focus-within .chat-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
.chat-reply.danger {
|
||||
color: var(--ed-danger);
|
||||
}
|
||||
.message-pinned {
|
||||
background: var(--ui-surface);
|
||||
}
|
||||
.pinned-messages {
|
||||
padding: 7px 10px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
background: var(--ui-surface);
|
||||
}
|
||||
.pinned-message {
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
color: var(--ed-fg);
|
||||
font-size: 11px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chat-reply-context {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
padding: 4px 6px;
|
||||
border-left: 2px solid var(--ed-accent);
|
||||
color: var(--ed-accent);
|
||||
font: 10px var(--font-data);
|
||||
}
|
||||
.chat-emote-picker {
|
||||
display: flex;
|
||||
max-height: 180px;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
margin-bottom: 6px;
|
||||
border: 1px solid var(--ui-line);
|
||||
background: var(--ed-bg);
|
||||
}
|
||||
.chat-emote-option {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr auto;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
text-align: left;
|
||||
}
|
||||
.chat-emote-option img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: 214px 1fr;
|
||||
min-height: calc(100vh - 42px);
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.side-panel {
|
||||
border-right: 1px solid var(--ui-divider);
|
||||
background: color-mix(in srgb, var(--ed-panel) 80%, var(--ed-bg));
|
||||
}
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 34px;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
color: var(--ed-fg);
|
||||
font: 600 13px var(--font-display);
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.panel-kicker,
|
||||
.micro {
|
||||
color: var(--ed-dim);
|
||||
font: 10px var(--font-data);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.server-card,
|
||||
.side-section {
|
||||
padding: 12px 10px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
}
|
||||
.server-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
margin: 13px 0 18px;
|
||||
font: 11px var(--font-data);
|
||||
}
|
||||
.status-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--ed-danger);
|
||||
}
|
||||
.status-dot.good {
|
||||
background: var(--ed-success);
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.control {
|
||||
width: 100%;
|
||||
padding: 7px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.control-button {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.auth-card {
|
||||
display: block;
|
||||
}
|
||||
.auth-card label {
|
||||
margin-top: 9px;
|
||||
}
|
||||
.auth-error {
|
||||
margin-top: 8px;
|
||||
color: var(--ed-warn);
|
||||
font: 10px/1.4 var(--font-data);
|
||||
}
|
||||
.auth-gate {
|
||||
min-height: 300px;
|
||||
}
|
||||
.auth-gate-body {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
align-content: start;
|
||||
gap: 8px;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 16px;
|
||||
color: var(--ed-dim);
|
||||
text-align: center;
|
||||
}
|
||||
.auth-gate-body strong {
|
||||
color: var(--ed-fg);
|
||||
font: 600 16px var(--font-display);
|
||||
}
|
||||
.credential-form {
|
||||
justify-items: stretch;
|
||||
text-align: left;
|
||||
}
|
||||
.credential-form strong {
|
||||
text-align: center;
|
||||
}
|
||||
.primary {
|
||||
padding: 7px 10px;
|
||||
border-color: var(--ui-line-focus);
|
||||
background: var(--ui-surface);
|
||||
color: var(--ed-fg);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.primary:hover {
|
||||
background: var(--ui-surface-hi);
|
||||
}
|
||||
.empty-row,
|
||||
.mono {
|
||||
margin-top: 8px;
|
||||
color: var(--ed-dim);
|
||||
font-size: 11px;
|
||||
}
|
||||
.oauth-actions {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: 9px;
|
||||
}
|
||||
.oauth-status-grid {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
padding-top: 9px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.oauth-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.oauth-status-row .good {
|
||||
color: var(--ed-green);
|
||||
}
|
||||
.secondary-action {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
color: var(--ed-dim);
|
||||
font-size: 10px;
|
||||
}
|
||||
.oauth-link-card {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.oauth-link {
|
||||
width: 100%;
|
||||
min-height: 72px;
|
||||
resize: vertical;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--ed-dim);
|
||||
font-size: 9px;
|
||||
}
|
||||
.oauth-hint {
|
||||
color: var(--ed-dim);
|
||||
font: 9px/1.4 var(--font-data);
|
||||
}
|
||||
.mono {
|
||||
font-family: var(--font-data);
|
||||
}
|
||||
.side-footer {
|
||||
margin-top: auto;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.bot-page {
|
||||
min-height: 100%;
|
||||
}
|
||||
.bot-page-body {
|
||||
padding: 18px;
|
||||
}
|
||||
.bot-command-list {
|
||||
margin-top: 12px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.bot-command-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding: 13px 4px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
}
|
||||
.bot-command-row strong {
|
||||
display: block;
|
||||
color: var(--ed-cyan);
|
||||
font-family: var(--font-data);
|
||||
}
|
||||
.bot-command-description {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--ed-dim);
|
||||
}
|
||||
.bot-command-meta {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
color: var(--ed-dim);
|
||||
font: 10px var(--font-data);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bot-secret-section {
|
||||
margin-top: 24px;
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.bot-secret-note {
|
||||
margin-top: 5px;
|
||||
color: var(--ed-dim);
|
||||
font-size: 11px;
|
||||
}
|
||||
.secret-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr auto;
|
||||
gap: 7px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.bot-secret-list {
|
||||
margin-top: 12px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.bot-secret-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 10px 4px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
}
|
||||
.bot-secret-row strong,
|
||||
.bot-secret-row span {
|
||||
display: block;
|
||||
}
|
||||
.overlay-page {
|
||||
min-height: 100%;
|
||||
}
|
||||
.overlay-toolbar,
|
||||
.overlay-controls {
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
}
|
||||
.overlay-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 1fr;
|
||||
min-height: calc(100vh - 100px);
|
||||
}
|
||||
.overlay-list {
|
||||
border-right: 1px solid var(--ui-divider);
|
||||
padding: 10px;
|
||||
}
|
||||
.overlay-list-item {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
gap: 4px;
|
||||
padding: 10px;
|
||||
color: var(--ed-dim);
|
||||
text-align: left;
|
||||
}
|
||||
.overlay-list-item.active,
|
||||
.overlay-list-item:hover {
|
||||
color: var(--ed-cyan);
|
||||
background: var(--ui-surface-hi);
|
||||
}
|
||||
.overlay-list-item span {
|
||||
font: 9px var(--font-data);
|
||||
}
|
||||
.overlay-editor {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 10px;
|
||||
padding: 14px;
|
||||
}
|
||||
.overlay-fields,
|
||||
.overlay-tabs {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
}
|
||||
.overlay-code {
|
||||
min-height: 110px;
|
||||
resize: vertical;
|
||||
color: var(--ed-dim);
|
||||
background: var(--ui-surface-low);
|
||||
font: 12px/1.4 var(--font-data);
|
||||
}
|
||||
.overlay-preview-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.overlay-preview {
|
||||
width: 100%;
|
||||
min-height: 180px;
|
||||
border: 1px solid var(--ui-divider);
|
||||
background: #101217;
|
||||
}
|
||||
.bot-secret-row span {
|
||||
margin-top: 3px;
|
||||
color: var(--ed-dim);
|
||||
font-size: 11px;
|
||||
}
|
||||
.version-label {
|
||||
display: block;
|
||||
margin-top: 13px;
|
||||
}
|
||||
.content-area {
|
||||
min-width: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
.dashboard-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 1fr 1fr;
|
||||
grid-template-rows: minmax(0, 1fr) 168px;
|
||||
gap: 0;
|
||||
min-height: calc(100vh - 96px);
|
||||
border-top: 1px solid var(--ed-edge);
|
||||
border-left: 1px solid var(--ed-edge);
|
||||
}
|
||||
.panel {
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
border-right: 1px solid var(--ed-edge);
|
||||
border-bottom: 1px solid var(--ed-edge);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: var(--ed-panel);
|
||||
}
|
||||
.preview-panel {
|
||||
grid-column: span 2;
|
||||
}
|
||||
.chat-panel {
|
||||
grid-row: span 2;
|
||||
}
|
||||
.preview-stage {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
min-height: 220px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
background: color-mix(in srgb, var(--ed-accent) 3%, var(--ed-bg));
|
||||
text-align: center;
|
||||
}
|
||||
.twitch-player {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 260px;
|
||||
flex: 1;
|
||||
border: 0;
|
||||
background: #000;
|
||||
}
|
||||
.offline-mark {
|
||||
color: var(--ed-fg);
|
||||
font: 700 24px var(--font-display);
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
.offline-subtitle {
|
||||
margin-top: 5px;
|
||||
color: var(--ed-dim);
|
||||
font: 10px var(--font-data);
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.panel-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 9px 10px;
|
||||
}
|
||||
.panel-tabs {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
padding: 9px 10px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
}
|
||||
.tab {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
padding: 0 0 5px;
|
||||
color: var(--ed-dim);
|
||||
font: 10px var(--font-data);
|
||||
}
|
||||
.tab.active {
|
||||
border-bottom: 2px solid var(--ed-accent);
|
||||
color: var(--ed-accent);
|
||||
}
|
||||
.empty-state,
|
||||
.chat-empty {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
justify-items: center;
|
||||
gap: 6px;
|
||||
min-height: 0;
|
||||
padding: 18px 12px;
|
||||
color: var(--ed-dim);
|
||||
text-align: center;
|
||||
}
|
||||
.empty-state strong,
|
||||
.chat-empty strong {
|
||||
color: var(--ed-fg);
|
||||
font: 600 14px var(--font-display);
|
||||
}
|
||||
.empty-state span,
|
||||
.chat-empty span {
|
||||
max-width: 260px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.empty-glyph {
|
||||
color: var(--ed-accent);
|
||||
font-size: 28px;
|
||||
}
|
||||
.activity-list {
|
||||
height: calc(100% - 70px);
|
||||
overflow: auto;
|
||||
}
|
||||
.activity-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 9px 12px;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
font: 10px var(--font-data);
|
||||
}
|
||||
.activity-type {
|
||||
color: var(--ed-fg);
|
||||
}
|
||||
.activity-time {
|
||||
color: var(--ed-dim);
|
||||
}
|
||||
.chat-composer {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 8px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.chat-identity-toggle,
|
||||
.chat-send-row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
.chat-identity {
|
||||
flex: 1;
|
||||
color: var(--ed-dim);
|
||||
font-size: 9px;
|
||||
}
|
||||
.chat-identity.active {
|
||||
border-color: var(--ui-line-focus);
|
||||
background: var(--ui-surface-hi);
|
||||
color: var(--ed-accent);
|
||||
}
|
||||
.chat-send-row .control {
|
||||
min-width: 0;
|
||||
}
|
||||
.chat-composer .control {
|
||||
min-width: 0;
|
||||
}
|
||||
.chat-composer .primary {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.quick-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
.quick-action {
|
||||
min-height: 52px;
|
||||
border: 1px solid var(--ui-line-soft);
|
||||
background: var(--ui-surface);
|
||||
color: var(--ed-dim);
|
||||
font: 10px var(--font-data);
|
||||
}
|
||||
.stream-editor {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 10px;
|
||||
border-top: 1px solid var(--ui-divider);
|
||||
}
|
||||
.stream-editor label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.category-options {
|
||||
display: grid;
|
||||
max-height: 180px;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--ui-line);
|
||||
background: var(--ed-bg);
|
||||
}
|
||||
.category-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
border-radius: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.quick-action:hover,
|
||||
.quick-action.accent {
|
||||
border-color: var(--ui-line-focus);
|
||||
color: var(--ed-fg);
|
||||
background: var(--ui-surface-hi);
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.workspace {
|
||||
grid-template-columns: 170px 1fr;
|
||||
}
|
||||
.dashboard-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
}
|
||||
.preview-panel {
|
||||
grid-column: span 2;
|
||||
}
|
||||
.chat-panel {
|
||||
grid-row: span 1;
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.top-nav {
|
||||
display: none;
|
||||
}
|
||||
.workspace {
|
||||
display: block;
|
||||
}
|
||||
.side-panel {
|
||||
display: none;
|
||||
}
|
||||
.content-area {
|
||||
padding: 10px;
|
||||
}
|
||||
.dashboard-grid {
|
||||
display: block;
|
||||
}
|
||||
.panel {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.chat-panel {
|
||||
min-height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.collaboration-search-panel {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.collaborator-results {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
margin: 8px 0 14px;
|
||||
}
|
||||
.collaborator-result {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--ui-divider);
|
||||
background: var(--ui-surface);
|
||||
}
|
||||
.collaborator-result .secondary {
|
||||
min-width: 28px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
.chat-thread-panel,
|
||||
.thread-index-panel {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.thread-index-row,
|
||||
.thread-reply {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--ui-divider);
|
||||
background: transparent;
|
||||
color: var(--ed-fg);
|
||||
text-align: left;
|
||||
}
|
||||
.thread-index-row:hover {
|
||||
background: var(--ui-surface-hi);
|
||||
}
|
||||
.thread-root {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--ui-line-focus);
|
||||
}
|
||||
.thread-replies {
|
||||
margin: 6px 0 10px 18px;
|
||||
border-left: 2px solid var(--ui-line-focus);
|
||||
}
|
||||
.overlay-diagnostics-panel {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.overlay-danger-panel {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.danger {
|
||||
border: 1px solid var(--ed-danger);
|
||||
background: transparent;
|
||||
color: var(--ed-danger);
|
||||
padding: 7px 12px;
|
||||
font: inherit;
|
||||
}
|
||||
.danger:hover {
|
||||
background: color-mix(in srgb, var(--ed-danger) 16%, transparent);
|
||||
}
|
||||
.diagnostics-toggle {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--ui-error, #ff6b7a);
|
||||
font: inherit;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.diagnostics-details {
|
||||
padding: 10px;
|
||||
color: var(--ui-error, #ff6b7a);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
export default {
|
||||
preprocess: vitePreprocess(),
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.svelte", "vite.config.ts"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
});
|
||||
Reference in New Issue
Block a user