106 Commits
Author SHA1 Message Date
ReaperOfVeriod ad3648ca6c Support completing first-run setup after session restoration
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 17:58:21 +01:00
ReaperOfVeriod 0c60409874 Keep credential replacement required for legacy session responses
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 17:56:08 +01:00
ReaperOfVeriod 8d8474c349 Keep keyring session payloads out of process arguments
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 17:52:22 +01:00
ReaperOfVeriod 7aef4171af Preserve native sessions during temporary server outages
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 17:51:10 +01:00
ReaperOfVeriod 58df8d06e1 refresh client implementation documentation
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 09:47:50 +01:00
ReaperOfVeriod bc06d323c7 preserve client session after partial load failure
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 09:44:07 +01:00
ReaperOfVeriod 092b58cdc2 allow manual connection retry during backoff
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 09:33:08 +01:00
ReaperOfVeriod b54c7b23fd open streamer channel from collab fallback
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:37:42 +01:00
ReaperOfVeriod 3dfeb98840 show guest star media state
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:34:59 +01:00
ReaperOfVeriod d81094861e type guest star media state
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:34:25 +01:00
ReaperOfVeriod 9bc0db7b2a use theme font fallbacks without missing assets
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:12:26 +01:00
ReaperOfVeriod aeae58d0d8 validate client server origins
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:57:31 +01:00
ReaperOfVeriod 313535fe70 add client lint command
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:53:53 +01:00
ReaperOfVeriod 9cf297613d Add fixed-source client update detection
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:34:37 +01:00
ReaperOfVeriod 8a3ac54204 Document overlay editor workflow
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:32:07 +01:00
ReaperOfVeriod d58d435fde Add safe overlay source completions
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:31:55 +01:00
ReaperOfVeriod ed30446136 Add tabbed line-numbered overlay editor
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:29:01 +01:00
ReaperOfVeriod 5920055473 Expose plugin runtime action
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:25:02 +01:00
ReaperOfVeriod 7fabf936b8 Add shared chat status popover
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:22:10 +01:00
ReaperOfVeriod 8879897e06 Show collaboration invitation state
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:21:48 +01:00
ReaperOfVeriod 8a28596f47 Add Guest Star participant controls
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:21:28 +01:00
ReaperOfVeriod 027e7434f6 Query collaboration capabilities
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:20:26 +01:00
ReaperOfVeriod 9f8a5a5ecc Expose overlay alert playback settings
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:18:37 +01:00
ReaperOfVeriod d1636a3a2a Show collaboration action notices
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:16:07 +01:00
ReaperOfVeriod b4bc860533 Confirm destructive collaboration actions
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:15:38 +01:00
ReaperOfVeriod 3d29bd5544 Expose plugin secret approval UI
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:14:57 +01:00
ReaperOfVeriod 7682abdc9f Add typed argument command editor
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:12:47 +01:00
ReaperOfVeriod a48cd5bc22 Add user-specific command rule editor
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:10:25 +01:00
ReaperOfVeriod 52a5ce895f Expose command web action configuration
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:05:48 +01:00
ReaperOfVeriod dc3985ee00 Add command simulation controls
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:03:24 +01:00
ReaperOfVeriod 1707731029 Add plugin rollback control
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:00:51 +01:00
ReaperOfVeriod cc5a36c086 Add plugin package installation UI
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:59:45 +01:00
ReaperOfVeriod d458f6b9e4 Document client authority boundary
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:56:04 +01:00
ReaperOfVeriod 1043d4057b Add authenticated backup restore control
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:54:58 +01:00
ReaperOfVeriod 733660e684 Add bot command variable autocomplete
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:54:05 +01:00
ReaperOfVeriod 7df188b3fd Add editable follow alert starter
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:52:51 +01:00
ReaperOfVeriod 6792544c16 Refresh collaboration state from realtime events
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:52:33 +01:00
ReaperOfVeriod 1325415085 Show Guest Star channel settings
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:50:54 +01:00
ReaperOfVeriod 1cf940c7dd Add dedicated collaboration dashboard
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:50:08 +01:00
ReaperOfVeriod 0881bb9b0f Format generated Tauri schemas
Client dependency and security checks / audit (push) Canceled after 0s
Client checks / checks (push) Canceled after 0s
2026-10-25 05:49:25 +01:00
ReaperOfVeriod f7d466eddb Restore native client sessions 2026-10-25 05:49:21 +01:00
ReaperOfVeriod 9167e04e77 Wire Tauri AppImage to native keyring
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:47:28 +01:00
ReaperOfVeriod 9854cc4c4f Ignore native build artifacts
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:43:49 +01:00
ReaperOfVeriod 761ff5305d Add Linux keyring storage boundary 2026-10-25 05:43:45 +01:00
ReaperOfVeriod d7dad3021f Complete bot command management controls
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:41:35 +01:00
ReaperOfVeriod a422cc70f0 Add bot command definition editor 2026-10-25 05:41:19 +01:00
ReaperOfVeriod f986cbb1e9 Add public bot command catalog 2026-10-25 05:39:09 +01:00
ReaperOfVeriod 3d2688c8be keep client reconnecting after outage 2026-10-25 05:38:28 +01:00
ReaperOfVeriod a387ea124a enforce client server compatibility 2026-10-25 05:38:06 +01:00
ReaperOfVeriod 02fcad6a8c add confirmed overlay deletion 2026-10-25 05:37:23 +01:00
ReaperOfVeriod 890c42e62f add overlay variable diagnostics 2026-10-25 05:36:50 +01:00
ReaperOfVeriod d095ddec03 confirm overlay draft discard 2026-10-25 05:36:16 +01:00
ReaperOfVeriod fa7ee4d87f make overlay draft recovery explicit 2026-10-25 05:35:19 +01:00
ReaperOfVeriod 000af8bfb1 connect to server on client startup 2026-10-25 05:34:55 +01:00
ReaperOfVeriod 950db9eb8c cache overlay drafts locally 2026-10-25 05:33:59 +01:00
ReaperOfVeriod 53756caea3 autosave overlay drafts after debounce 2026-10-25 05:33:41 +01:00
ReaperOfVeriod 87ceb8c87f consume server compatibility metadata 2026-10-25 05:32:10 +01:00
ReaperOfVeriod 953dfaaad0 preserve chat replies and threads 2026-10-25 05:30:51 +01:00
ReaperOfVeriod 46fb8e2cc3 add collaborator search controls 2026-10-25 05:28:00 +01:00
ReaperOfVeriod 8b8ba4b618 load collaboration favorites 2026-10-25 05:24:56 +01:00
ReaperOfVeriod 209f2ed7e0 add client issue and release scaffolding 2026-10-25 05:21:26 +01:00
ReaperOfVeriod 9d16dcebd5 run client quality gates in CI 2026-10-25 05:20:59 +01:00
ReaperOfVeriod 7e9f571df1 add backup controls to settings 2026-10-25 05:20:32 +01:00
ReaperOfVeriod 0fa40fc63d add overlay enabled controls 2026-10-25 05:15:14 +01:00
ReaperOfVeriod 5d6af050ad add overlay editor and preview 2026-10-25 05:13:42 +01:00
ReaperOfVeriod c1df6938b5 add bot secret manager view 2026-10-25 05:12:15 +01:00
ReaperOfVeriod 450503afc3 add bot command catalog view 2026-10-25 05:09:25 +01:00
ReaperOfVeriod 85cd8aee00 load shared chat state 2026-10-25 05:06:24 +01:00
ReaperOfVeriod ba84f9d778 show OAuth scope status 2026-10-25 05:05:30 +01:00
ReaperOfVeriod c256ce36e5 add collaboration invite controls 2026-10-25 05:04:11 +01:00
ReaperOfVeriod 1ba93cce8a add Guest Star collaboration panel 2026-10-25 05:02:51 +01:00
ReaperOfVeriod 0b2fb65af3 filter activity feed by category 2026-10-25 05:01:46 +01:00
ReaperOfVeriod 181ad46c29 add chat pin and delete actions 2026-10-25 05:01:32 +01:00
ReaperOfVeriod d20af42053 link chat users to moderation controls 2026-10-25 05:01:11 +01:00
ReaperOfVeriod ba3d3b2d80 render Twitch emote fragments in chat 2026-10-25 05:00:51 +01:00
ReaperOfVeriod 1b7c41ea07 add Twitch style emote autocomplete 2026-10-25 05:00:31 +01:00
ReaperOfVeriod 810589647b add Twitch chat reply controls 2026-10-25 04:59:33 +01:00
ReaperOfVeriod 7f0d3fbb6d render incoming Twitch chat 2026-10-25 04:59:08 +01:00
ReaperOfVeriod c6844fcc47 show server diagnostics in client 2026-10-25 04:55:01 +01:00
ReaperOfVeriod 36b99968b6 add Twitch identity disconnect controls 2026-10-25 04:54:15 +01:00
ReaperOfVeriod aa16fc29d3 add administrator notification bell 2026-10-25 04:53:31 +01:00
ReaperOfVeriod 0c684d4831 feat: add moderation controls 2026-10-25 04:49:52 +01:00
ReaperOfVeriod 2eb050294b feat: add streamer and bot chat sending 2026-10-25 04:48:17 +01:00
ReaperOfVeriod 93d34306bc feat: embed Twitch stream player 2026-10-25 04:47:28 +01:00
ReaperOfVeriod 625084cfa5 feat: add debounced Twitch category picker 2026-10-25 04:47:08 +01:00
ReaperOfVeriod c81748da2c feat: add stream information editor 2026-10-25 04:46:12 +01:00
ReaperOfVeriod f48618f94b feat: render activity history and live events 2026-10-25 04:44:43 +01:00
ReaperOfVeriod 5cb893af62 feat: connect client to realtime WebSocket 2026-10-25 04:42:25 +01:00
ReaperOfVeriod c32582a1cd feat: add client reconnect scheduling 2026-10-25 04:41:28 +01:00
ReaperOfVeriod 05d7947a29 feat: add Twitch account authorization controls 2026-10-25 04:37:08 +01:00
ReaperOfVeriod 8c8d632557 docs: describe client auth development behavior 2026-10-25 04:24:11 +01:00
ReaperOfVeriod 9ea2899ca7 feat: add client administrator authentication gate 2026-10-25 04:24:07 +01:00
ReaperOfVeriod e2076cb044 fix: preserve desktop dashboard panel layout 2026-10-25 04:06:52 +01:00
ReaperOfVeriod b5a6a61e2d style: restore dashboard grid rules 2026-10-25 04:06:10 +01:00
ReaperOfVeriod 71db966697 fix: keep top level dashboard navigation 2026-10-25 04:05:47 +01:00
ReaperOfVeriod 5d41473f47 style: adopt mewlody workspace shell 2026-10-25 04:05:27 +01:00
ReaperOfVeriod 35df7afe88 style: compact empty dashboard states 2026-10-25 04:04:56 +01:00
ReaperOfVeriod 512757ca4a style: remove redundant dashboard heading 2026-10-25 04:04:19 +01:00
ReaperOfVeriod 0003734bed style: compact dashboard heading 2026-10-25 04:04:06 +01:00
ReaperOfVeriod 694bfeabf3 style: follow albedo panel geometry 2026-10-25 04:03:46 +01:00
ReaperOfVeriod fa3915bc5d style: match albedo panel composition 2026-10-25 04:00:22 +01:00
ReaperOfVeriod 998b3d3072 fix: align controls with albedo style rules 2026-10-25 03:59:05 +01:00
ReaperOfVeriod 66fe3e4fea feat: establish albedo dashboard shell 2026-10-25 03:57:56 +01:00
ReaperOfVeriod 5298c6870b feat: add initial client shell
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 03:52:37 +01:00
ReaperOfVeriod 67a8a06268 chore: document branch workflow and security checks
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s
2026-10-25 03:48:58 +01:00
ReaperOfVeriod 51c4853d51 chore: add MIT license
Client checks / checks (push) Canceled after 0s
2026-10-25 03:45:43 +01:00
49 changed files with 16383 additions and 4 deletions
+17
View File
@@ -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
+18
View File
@@ -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
+15
View File
@@ -9,4 +9,19 @@ 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 lint
- run: npm run check
- run: npm test
- run: npm run build
- uses: actions-rust-lang/setup-rust-toolchain@v1
- working-directory: native
run: cargo fmt -- --check
- working-directory: native
run: cargo test
+18
View File
@@ -0,0 +1,18 @@
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 lint && npm run check && npm run build
- run: npm test
+16
View File
@@ -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
+2
View File
@@ -1,4 +1,6 @@
node_modules/
native/target/
src-tauri/target/
dist/
build/
coverage/
-1
View File
@@ -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.
+8
View File
@@ -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.
+5
View File
@@ -1,4 +1,9 @@
# Contributing
Run formatting, linting, type checks, tests, and the AppImage build before submission. Use deterministic mocked server/API fixtures.
The client uses Prettier for formatting and `svelte-check` for the lint/check
gate. Run `npm run format:check`, `npm run lint`, `npm run check`, and
`npm run build` before pushing changes.
The server address is treated as untrusted input. Client requests accept only
HTTP(S) origins without embedded credentials, query strings, or fragments.
+21
View File
@@ -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.
+17 -2
View File
@@ -2,9 +2,24 @@
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/` crate defines the AppImage storage
boundary: `settings.json` contains only the server address and username, while
the session credential is stored in the Linux kernel keyring with no plaintext
fallback.
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.
Implementation has not started. See the parent workspace's `PLAN.md`, `TODO.md`, and `AGENTS.md` before beginning work.
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.
The client currently includes the live dashboard shell, server connection and
retry flow, administrator authentication, Twitch identity controls, chat and
moderation surfaces, bot/overlay/plugin administration, and the native keyring
storage boundary. Remaining live Twitch acceptance work is tracked in the
parent workspace's `TODO.md` and operator checklist.
Consumes the versioned contracts from the sibling `trixie-api` repository.
-1
View File
@@ -1,4 +1,3 @@
# Security
Report vulnerabilities privately through the hosting instance. Never include credentials, tokens, or real user data in issues or fixtures.
+35
View File
@@ -0,0 +1,35 @@
# Client boundary
The Linux client is a Svelte/Tauri control surface for `trixie-server`. It
renders server-owned state and sends explicit user actions through the shared
versioned HTTP/WebSocket boundary.
The client never stores Twitch refresh tokens or the administrator password,
calls Twitch directly, executes plugin code, or treats local UI state as
authoritative. Local overlay drafts and display preferences are recoverable
presentation data only; the server remains authoritative for publication,
authorization, Twitch state, and durable history.
# Saved-session recovery
Native startup verifies the keyring session before loading authenticated data.
HTTP 401/403 clears the saved session and requests login. Server failures retain
the credential and retry immediately, then after 3, 10, 15, and repeatedly 30
seconds. Manual login and logout cancel pending restoration. Dashboard-loading
failures do not invalidate a successfully verified session.
Restored sessions retain the server's mandatory credential-replacement state.
An omitted replacement flag is treated conservatively as requiring setup rather
than silently allowing the dashboard to consider setup complete.
Dashboard loading and realtime connection wait until credential setup is complete;
the server independently enforces the same operational-access restriction.
The setup form includes a current-password field because restored keyring
sessions intentionally do not restore the user's password. This lets setup
continue after a client restart without storing the password on disk.
Run `node --test src/session-recovery.test.js` for response classification and
retry-schedule coverage. This is not a substitute for native AppImage acceptance.
Keyring writes use `keyctl padd` with the session supplied through standard input,
never process arguments. Helper stdout/stderr are discarded so credential payloads
cannot enter diagnostics. Native tests cover the argument boundary, stdin delivery,
and helper failure handling without reading or modifying real keyring entries.
+13
View File
@@ -0,0 +1,13 @@
# Overlay editor
The Overlays view edits HTML, CSS, and JavaScript independently. Each source
tab shows line numbers and a small completion picker containing literal starter
snippets. Selecting a completion only inserts text into the active draft; it
does not execute or evaluate the snippet in the client.
Draft changes are cached locally and autosaved to the server after the debounce
period. The live preview uses a sandboxed iframe. Publishing is a separate
explicit action that changes the OBS browser-source output; saving a draft does
not publish it. Diagnostics remain available through the warning indicator,
and the server applies the authoritative size, slug, and browser-source
security checks.
+11
View File
@@ -0,0 +1,11 @@
# Client updates
The Settings view checks the fixed stable release source for
`ReaperOfVeriod/trixy-linux-client` on Gitea. It compares the release tag with
the running client version and shows the release page when a newer version is
available.
Applying an update remains manual: review the release, download the x86-64
AppImage, replace the existing file, and restart it. The client never accepts
an arbitrary update URL, silently replaces itself, or stores credentials in the
update flow.
+12
View File
@@ -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>
+284
View File
@@ -0,0 +1,284 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bitflags"
version = "2.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "libc"
version = "0.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rustix"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "trixy-native"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
"sha2",
"tempfile",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "trixy-native"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Native Linux storage boundary for the Trixie client"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
[dev-dependencies]
tempfile = "3"
+12
View File
@@ -0,0 +1,12 @@
# Native credential boundary
This crate is the storage boundary used by the future Linux AppImage shell.
`Settings` writes only `server_address` and `username` to the client
`settings.json`. Session credentials are stored under the Linux kernel
keyring service `trixie.server.session`, scoped by server address and
username. `load_session` returns `None` when the keyring is unavailable;
callers must return to credential entry and must not create a plaintext
fallback.
The crate uses `keyctl` with argument arrays, never a shell command. It does
not log or serialize session values.
+191
View File
@@ -0,0 +1,191 @@
//! Native persistence boundary for the Linux client.
//!
//! Settings are deliberately limited to non-secret connection metadata. The
//! session credential is stored as a `user` key in the Linux kernel keyring
//! through the `keyctl` utility. If keyring access fails, callers receive an
//! error and must ask for credentials again; no plaintext fallback is allowed.
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{
fs, io,
io::Write,
path::{Path, PathBuf},
process::{Command, Stdio},
};
pub const KEYRING_SERVICE: &str = "trixie.server.session";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Settings {
pub server_address: String,
pub username: String,
}
impl Settings {
pub fn load(path: impl AsRef<Path>) -> io::Result<Self> {
let bytes = fs::read(path)?;
serde_json::from_slice(&bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}
pub fn save(&self, path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let bytes = serde_json::to_vec_pretty(self)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let temporary = path.with_extension("json.tmp");
fs::write(&temporary, bytes)?;
fs::rename(temporary, path)
}
}
pub fn default_settings_path() -> PathBuf {
let base = std::env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".config")))
.unwrap_or_else(|| PathBuf::from("."));
base.join("trixie").join("settings.json")
}
fn key_description(settings: &Settings) -> String {
let mut digest = Sha256::new();
digest.update(settings.server_address.as_bytes());
digest.update([0]);
digest.update(settings.username.as_bytes());
let digest = digest.finalize();
format!("{KEYRING_SERVICE}:{}", hex_encode(&digest))
}
fn hex_encode(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut result = String::with_capacity(bytes.len() * 2);
for byte in bytes {
result.push(HEX[(byte >> 4) as usize] as char);
result.push(HEX[(byte & 0x0f) as usize] as char);
}
result
}
fn keyctl(args: &[&str]) -> io::Result<Vec<u8>> {
let output = Command::new("keyctl").args(args).output()?;
if !output.status.success() {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Linux kernel keyring access failed",
));
}
Ok(output.stdout)
}
pub fn store_session(settings: &Settings, session: &str) -> io::Result<()> {
if session.is_empty() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty session"));
}
let description = key_description(settings);
write_key_payload(key_write_command(&description), session.as_bytes())
}
// Secret payloads must never be arguments: process listings can expose argv.
fn key_write_command(description: &str) -> Command {
let mut command = Command::new("keyctl");
command.args(["padd", "user", description, "@s"]);
command
}
fn write_key_payload(mut command: Command, payload: &[u8]) -> io::Result<()> {
let mut child = command
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
let result = child.stdin.take().expect("piped stdin").write_all(payload);
// Closing stdin above signals EOF. Reap the child even if writing failed.
let status = child.wait()?;
result?;
if status.success() {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Linux kernel keyring access failed",
))
}
}
pub fn load_session(settings: &Settings) -> io::Result<Option<String>> {
let description = key_description(settings);
let id = match keyctl(&["search", "@s", "user", &description]) {
Ok(value) => String::from_utf8_lossy(&value).trim().to_string(),
Err(_) => return Ok(None),
};
if id.is_empty() {
return Ok(None);
}
let value = keyctl(&["pipe", &id])?;
String::from_utf8(value)
.map(Some)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}
pub fn clear_session(settings: &Settings) -> io::Result<()> {
let description = key_description(settings);
let id = String::from_utf8_lossy(&keyctl(&["search", "@s", "user", &description])?)
.trim()
.to_string();
if id.is_empty() {
return Ok(());
}
keyctl(&["unlink", &id, "@s"]).map(|_| ())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_write_arguments_contain_only_metadata() {
let command = key_write_command("test-description");
let args: Vec<_> = command.get_args().collect();
assert_eq!(args, ["padd", "user", "test-description", "@s"]);
}
#[test]
fn payload_is_delivered_via_stdin_and_failures_are_reported() {
let mut receiver = Command::new("sh");
receiver.args(["-c", "test \"$(cat)\" = fixture-payload"]);
write_key_payload(receiver, b"fixture-payload").unwrap();
let mut failure = Command::new("sh");
failure.args(["-c", "cat >/dev/null; exit 1"]);
assert!(write_key_payload(failure, b"fixture-payload").is_err());
}
fn settings() -> Settings {
Settings {
server_address: "https://trixie.example".into(),
username: "admin".into(),
}
}
#[test]
fn settings_round_trip_contains_no_password_field() {
let directory = tempfile::tempdir().expect("temporary directory");
let path = directory.path().join("settings.json");
settings().save(&path).expect("save settings");
let text = fs::read_to_string(&path).expect("read settings");
assert!(!text.contains("password"));
assert_eq!(Settings::load(path).expect("load settings"), settings());
}
#[test]
fn key_description_is_stable_but_scoped_to_connection_and_user() {
let first = key_description(&settings());
assert_eq!(first, key_description(&settings()));
let mut different = settings();
different.username = "moderator".into();
assert_ne!(first, key_description(&different));
assert!(first.starts_with(KEYRING_SERVICE));
}
}
+1785
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "trixy-linux-client",
"private": true,
"version": "0.1.0-dev",
"type": "module",
"scripts": {
"test": "node --test src/*.test.js",
"dev": "vite",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"build": "vite build",
"check": "svelte-check --tsconfig ./tsconfig.json",
"lint": "svelte-check --tsconfig ./tsconfig.json",
"format": "prettier --write .",
"format:check": "prettier --check ."
},
"dependencies": {
"@tauri-apps/api": "^2.0.0"
},
"devDependencies": {
"@tsconfig/svelte": "^5.0.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tauri-apps/cli": "^2.0.0",
"prettier": "^3.5.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.7.0",
"vite": "^6.0.0"
}
}
+1
View File
@@ -0,0 +1 @@
target/
+4305
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "trixy-linux-client"
version = "0.1.0"
description = "Trixie Linux client"
authors = ["ReaperOfVeriod"]
license = "MIT"
edition = "2021"
[lib]
name = "trixy_linux_client_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri = { version = "2", features = [] }
trixy-native = { path = "../native" }
+3
View File
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="20" fill="#081018"/>
<path d="M64 18 104 64 64 110 24 64Z" fill="none" stroke="#00b9f2" stroke-width="8"/>
<path d="M64 36 88 64 64 92 40 64Z" fill="#182b3a" stroke="#00b9f2" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 305 B

+65
View File
@@ -0,0 +1,65 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::path::PathBuf;
use trixy_native::{clear_session, default_settings_path, load_session, store_session, Settings};
fn error_message(error: std::io::Error) -> String {
error.to_string()
}
#[tauri::command]
fn load_client_settings() -> Result<Settings, String> {
let path = default_settings_path();
if !path.exists() {
return Ok(Settings {
server_address: "http://127.0.0.1:8787".to_string(),
username: String::new(),
});
}
Settings::load(path).map_err(error_message)
}
#[tauri::command]
fn save_client_settings(settings: Settings) -> Result<(), String> {
if settings.server_address.trim().is_empty() || settings.username.trim().is_empty() {
return Err("Server address and username are required.".to_string());
}
Settings::save(&settings, default_settings_path()).map_err(error_message)
}
#[tauri::command]
fn load_server_session(settings: Settings) -> Result<Option<String>, String> {
load_session(&settings).map_err(error_message)
}
#[tauri::command]
fn store_server_session(settings: Settings, session: String) -> Result<(), String> {
store_session(&settings, &session).map_err(error_message)
}
#[tauri::command]
fn clear_server_session(settings: Settings) -> Result<(), String> {
clear_session(&settings).map_err(error_message)
}
#[tauri::command]
fn client_settings_path() -> String {
PathBuf::from(default_settings_path())
.to_string_lossy()
.into_owned()
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
load_client_settings,
save_client_settings,
load_server_session,
store_server_session,
clear_server_session,
client_settings_path
])
.run(tauri::generate_context!())
.expect("error while running Trixie client");
}
+3
View File
@@ -0,0 +1,3 @@
fn main() {
trixy_linux_client_lib::run();
}
+35
View File
@@ -0,0 +1,35 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Trixie",
"version": "0.1.0",
"identifier": "nl.reaperofveriod.trixie",
"build": {
"beforeDevCommand": "npm run dev -- --host 127.0.0.1",
"devUrl": "http://127.0.0.1:5173",
"beforeBuildCommand": "npm run build",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "Trixie",
"width": 1366,
"height": 768,
"minWidth": 1024,
"minHeight": 640,
"resizable": true
}
],
"security": {
"csp": "default-src 'self' http://127.0.0.1:8787 ws://127.0.0.1:8787 https://*.twitch.tv https://*.twitchcdn.net https://cdn.7tv.app; connect-src 'self' http: https: ws: wss:; img-src 'self' data: https:; frame-src https://player.twitch.tv; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'"
}
},
"bundle": {
"active": true,
"icon": ["icons/icon.png", "icons/icon.svg"],
"targets": ["appimage"],
"linux": {
"deb": { "depends": [] }
}
}
}
+1793
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -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;
+14
View File
@@ -0,0 +1,14 @@
/** Classify authoritative rejection separately from temporary server failures.
* @param {number} status HTTP response status.
*/
export function sessionResponseState(status) {
if (status === 401 || status === 403) return 'revoked';
return status >= 200 && status < 300 ? 'valid' : 'unavailable';
}
/** Milliseconds before each retry; attempt zero is the immediate first retry.
* @param {number} attempt Zero-based retry index.
*/
export function sessionRetryDelay(attempt) {
return [0, 3000, 10000, 15000][attempt] ?? 30000;
}
+17
View File
@@ -0,0 +1,17 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { sessionResponseState, sessionRetryDelay } from './session-recovery.js';
test('only explicit authentication rejection invalidates a saved session', () => {
for (const status of [401, 403]) assert.equal(sessionResponseState(status), 'revoked');
for (const status of [404, 429, 500, 502, 503])
assert.equal(sessionResponseState(status), 'unavailable');
assert.equal(sessionResponseState(200), 'valid');
});
test('saved session retries follow the shared recovery schedule', () => {
assert.deepEqual(
Array.from({ length: 7 }, (_, attempt) => sessionRetryDelay(attempt)),
[0, 3000, 10000, 15000, 30000, 30000, 30000],
);
});
+1118
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default {
preprocess: vitePreprocess(),
};
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"noEmit": true,
"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"]
}
+6
View File
@@ -0,0 +1,6 @@
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [svelte()],
});