asdf98 commited on
Commit
c8f5ae1
·
verified ·
1 Parent(s): 3891bc2

Phase 0 MUSE alpha skeleton and SRS docs

Browse files
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ node_modules/
2
+ dist/
3
+ src-tauri/target/
4
+ src-tauri/gen/
5
+ .DS_Store
6
+ *.log
7
+ .env
README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MUSE Alpha
2
+
3
+ Creative browser prototype: Tauri v2 + Rust + SolidJS.
4
+
5
+ This repo currently contains:
6
+ - `docs/MUSE_SRS_v3.md` — Master Software Requirements Specification v3.0
7
+ - `docs/MUSE_SRS_v4.md` — Master SRS v4.0 implementation guide
8
+ - Phase 0 implementation skeleton: custom titlebar, sidebar, onboarding, 8-theme system, home page, settings store, and SQLite migration setup.
9
+
10
+ ## Phase 0 status
11
+
12
+ Implemented from current Tauri v2/SolidJS docs:
13
+ - Tauri v2 app skeleton (`src-tauri/`)
14
+ - SolidJS frontend (`src/`)
15
+ - Custom undecorated window titlebar
16
+ - Sidebar navigation shell
17
+ - Home page with greeting and static URL/search bar
18
+ - Theme token system with 8 presets
19
+ - Onboarding flow using Tauri Store
20
+ - SQLite plugin with Phase 0 migration
21
+ - Capabilities configured for window controls, store, SQL, FS, clipboard, stronghold/global shortcut preparation
22
+
23
+ ## Dev
24
+
25
+ ```bash
26
+ pnpm install
27
+ pnpm tauri dev
28
+ ```
29
+
30
+ Frontend-only build:
31
+
32
+ ```bash
33
+ pnpm build
34
+ ```
docs/MUSE_SRS_v3.md ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MUSE — Master Software Requirements Specification v3.0
2
+ ### Creative Browser · Tauri v2 + Rust · Complete Product Definition
3
+
4
+ > Canonical v3 requirements document supplied by the product owner in the implementation prompt.
5
+
6
+ This document defines the full Muse product vision, architecture, browser fundamentals, ad-block v3, creative modules, artist-exclusive features, UI/UX system, business model, roadmap, and non-goals.
7
+
8
+ ## Document map
9
+
10
+ Incorporates and supersedes:
11
+ - SRS v1.0 (original vision + market research)
12
+ - SRS v2.0 (deep UX/UI + themes + ad-block)
13
+ - 01-DATABASE-ARCHITECTURE.md
14
+ - 02-ADBLOCK-INTEGRATION.md
15
+ - 03-RUST-ARCHITECTURE.md
16
+ - 04-USER-RESEARCH.md
17
+ - 05-PLATFORM-SPECIFIC.md
18
+ - 06-MOBILE-TABLET-UX.md
19
+ - 07-ALGORITHMS-THIRDPARTY.md
20
+ - 08-UI-UX-COMPLETE.md
21
+
22
+ ## Core mission
23
+
24
+ Muse is the first browser designed specifically for the visual artist's creative loop:
25
+
26
+ ```text
27
+ Discover → Collect → Organize → Reference → Create → Repeat
28
+ ```
29
+
30
+ Muse replaces the fragmented five-app workflow:
31
+ - Chrome/Firefox: full browser
32
+ - PureRef: Board module / infinite reference canvas
33
+ - Eagle App: Library module
34
+ - Pinterest/Are.na: Boards + Library without algorithm/account/subscription
35
+ - Standalone color picker: integrated extraction, picker, search, export
36
+
37
+ ## Locked non-goals
38
+
39
+ - No AI image generation
40
+ - No subscription model
41
+ - No hosted cloud storage
42
+ - No social feed or algorithm
43
+ - No drawing tools
44
+ - No real-time collaboration for v1 scope
45
+ - No telemetry without explicit consent
46
+ - No bundled Chromium
47
+ - No mandatory account
48
+
49
+ ## Architecture summary
50
+
51
+ Stack:
52
+ - Tauri v2 + Rust core
53
+ - SolidJS shell frontend
54
+ - System WebViews for browser tabs
55
+ - SQLite + WAL for local data
56
+ - Stronghold for encrypted credentials
57
+ - `tauri-plugin-store` for settings
58
+ - `brave/adblock-rust` for filter-rule engine
59
+
60
+ Rust module plan:
61
+ - `browser/` tab lifecycle, navigation, injection, tab sleep, find
62
+ - `adblock/` engine, filter lists, user rules, subscriptions, stats, cosmetic injection
63
+ - `library/` SQLite CRUD, thumbnails, color extraction, dedupe, FTS5 search, smart folders
64
+ - `board/` infinite canvas, autosave, items, export, annotations
65
+ - `credentials/` vault, autofill, generator
66
+ - `downloads/`, `cookies/`, `sessions/`, `permissions/`, `privacy/`, `color/`, `workspace/`
67
+
68
+ ## Database summary
69
+
70
+ SQLite with startup PRAGMAs:
71
+
72
+ ```sql
73
+ PRAGMA journal_mode = WAL;
74
+ PRAGMA synchronous = NORMAL;
75
+ PRAGMA mmap_size = 268435456;
76
+ PRAGMA cache_size = -16384;
77
+ PRAGMA temp_store = MEMORY;
78
+ PRAGMA foreign_keys = ON;
79
+ PRAGMA busy_timeout = 5000;
80
+ PRAGMA auto_vacuum = INCREMENTAL;
81
+ ```
82
+
83
+ New v3 tables include:
84
+ - `sessions`, `session_tabs`
85
+ - `credentials` pointers to Stronghold keys
86
+ - `downloads`
87
+ - `cookie_rules`
88
+ - `site_permissions`
89
+ - `site_zoom`
90
+ - `adblock_subscriptions`
91
+ - `adblock_user_rules`
92
+ - `board_annotations`
93
+ - `study_sessions`
94
+
95
+ ## Browser fundamentals covered
96
+
97
+ - Cookie management
98
+ - Password and credential manager
99
+ - Download manager
100
+ - Tab sleep
101
+ - Session management
102
+ - Find in page
103
+ - Per-site zoom memory
104
+ - Permission system
105
+ - HTTPS-first and safe browsing without Google URL API
106
+ - DNS over HTTPS
107
+ - Print
108
+ - Autofill
109
+ - WebRTC and fingerprint protection
110
+ - Offline/error pages
111
+ - Media autoplay policy
112
+ - Certificate/security info
113
+
114
+ ## Ad-block v3
115
+
116
+ Hybrid design in v3:
117
+ - Navigation blocking
118
+ - Native network blocking where available
119
+ - Cosmetic filtering
120
+ - Built-in multi-list subscriptions
121
+ - Custom uBlock-syntax rules
122
+ - Statistics and transparency
123
+ - Cookie consent auto-denial
124
+
125
+ Built-in lists:
126
+ - EasyList
127
+ - EasyPrivacy
128
+ - uBlock filters
129
+ - uBlock Unbreak
130
+ - Fanboy Annoyances
131
+ - Peter Lowe
132
+ - Malware Domains
133
+ - AdGuard Base
134
+ - AdGuard Social
135
+ - IndianList
136
+
137
+ ## Core creative modules
138
+
139
+ - Home Page
140
+ - URL Bar System
141
+ - Sidebar & Tab System
142
+ - Library Module
143
+ - Board Module / PureRef replacement
144
+ - Split View System
145
+ - Color Tools
146
+ - Image Hover Overlay
147
+
148
+ ## Artist-exclusive v3 modules
149
+
150
+ - Quick Study Mode
151
+ - Board annotations/markup
152
+ - Rotation, flip, light table, overlays
153
+ - Proportion and grid overlays
154
+ - Source site intelligence profiles
155
+ - Library timeline view
156
+ - Color export for artist apps
157
+ - Reference sheet export
158
+ - Workspace sessions for projects
159
+
160
+ ## UI/UX system
161
+
162
+ Theme presets:
163
+ - Dusk
164
+ - Parchment
165
+ - Midnight
166
+ - Studio
167
+ - Moss
168
+ - Rose
169
+ - Obsidian
170
+ - Linen
171
+
172
+ Typography:
173
+ - Geist UI
174
+ - Lora reader body
175
+ - Geist Mono for code/dimensions
176
+
177
+ Onboarding:
178
+ 1. Welcome
179
+ 2. Make it yours (name + theme)
180
+ 3. Name first workspace
181
+
182
+ Keyboard/command palette includes standard browser shortcuts plus Muse creative shortcuts.
183
+
184
+ ## Business model
185
+
186
+ - Free tier forever
187
+ - Muse one-time purchase
188
+ - Muse Pro one-time upgrade
189
+ - Pay-What-You-Want launch window
190
+ - No subscription
191
+
192
+ ## Roadmap
193
+
194
+ - Phase 0: Foundation
195
+ - Phase 1: Browser basics
196
+ - Phase 2: Browser fundamentals complete
197
+ - Phase 3: Creative core
198
+ - Phase 4: Power features
199
+ - Phase 5: Ship Windows
200
+ - Phase 6: Cross-platform
201
+ - Phase 7: Ecosystem
202
+
203
+ ## Note
204
+
205
+ The original full v3 text was supplied in-chat and is treated as the requirements source for this repository. Phase 0 implementation begins from the v4 implementation guide while preserving these v3 product requirements.
docs/MUSE_SRS_v4.md ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MUSE — Master SRS v4.0
2
+ ## Creative Browser · Tauri v2 + Rust · Complete Implementation Guide
3
+
4
+ > One window. Multiple child webviews. One ad-block engine. All platforms.
5
+
6
+ This implementation guide supersedes earlier architecture/code notes for v1.0 implementation sequencing.
7
+
8
+ ## Critical architecture decisions
9
+
10
+ ### 1. Single OS window + child webviews for tabs
11
+
12
+ Muse is one OS window containing:
13
+ - Shell WebView: SolidJS UI (titlebar, sidebar, URL bar, home, library, boards)
14
+ - Child tab WebViews: one per browser tab
15
+
16
+ Implemented later with `window.add_child()` behind Tauri's `unstable` feature. Phase 0 does **not** use child WebViews yet.
17
+
18
+ ### 2. Cross-platform ad-block = Rust engine + JS injection
19
+
20
+ The v4 guide deliberately removes platform-native WebView2/WKContentRuleList/WebKitGTK-specific blocking from the v1 cross-platform plan.
21
+
22
+ Ad-block layers:
23
+ - Layer 0: Navigation handler in Rust
24
+ - Layer 1: JS fetch/XHR override
25
+ - Layer 2: DOM CSP injection
26
+ - Layer 3: Cosmetic CSS injection
27
+ - Layer 4: Scriptlet injection
28
+
29
+ ### 3. `unstable` feature is acceptable for desktop tabs
30
+
31
+ `tauri = { version = "2", features = ["unstable"] }` is planned once Phase 1 implements child tab WebViews.
32
+
33
+ ### 4. Mobile tab model differs
34
+
35
+ Mobile uses conceptual tab state plus `WebviewWindow` until Tauri supports child webviews on mobile. Desktop Windows remains v1 target.
36
+
37
+ ## Repository structure target
38
+
39
+ ```text
40
+ muse/
41
+ ├── package.json
42
+ ├── vite.config.ts
43
+ ├── index.html
44
+ ├── src/
45
+ │ ├── index.tsx
46
+ │ ├── App.tsx
47
+ │ ├── components/
48
+ │ ├── store/
49
+ │ └── styles/
50
+ └── src-tauri/
51
+ ├── Cargo.toml
52
+ ├── build.rs
53
+ ├── tauri.conf.json
54
+ ├── capabilities/default.json
55
+ ├── migrations/
56
+ └── src/
57
+ ├── main.rs
58
+ ├── lib.rs
59
+ ├── state.rs
60
+ ├── settings.rs
61
+ ├── browser/
62
+ ├── adblock/
63
+ ├── library/
64
+ ├── board/
65
+ ├── credentials/
66
+ ├── downloads/
67
+ ├── sessions/
68
+ └── db/
69
+ ```
70
+
71
+ ## Phase 0 implementation scope
72
+
73
+ Deliverable: running app with custom titlebar, theme, and sidebar navigation.
74
+
75
+ User can:
76
+ - Open Muse on Windows
77
+ - See Dusk theme, custom titlebar, sidebar
78
+ - Click sidebar icons (Library/Board placeholders)
79
+ - See Home page with greeting and static URL bar
80
+ - Switch all 8 themes
81
+ - Complete onboarding in under 60 seconds
82
+
83
+ Not included yet:
84
+ - Real browser tabs
85
+ - Ad-block
86
+ - Library/Board functionality
87
+
88
+ Phase 0 work items:
89
+ - SolidJS Tauri app skeleton
90
+ - Tauri v2 `Cargo.toml`
91
+ - `tauri.conf.json` with undecorated window
92
+ - Capabilities file
93
+ - Custom titlebar with window controls
94
+ - Sidebar shell
95
+ - Home page shell
96
+ - Theme token CSS for 8 themes
97
+ - App state module
98
+ - SQLite init and migration
99
+ - Tauri Store settings
100
+ - Onboarding flow
101
+
102
+ ## Phase 1 target
103
+
104
+ Real multi-tab browser:
105
+ - `TabManager`
106
+ - `window.add_child()` child WebViews
107
+ - tab create/close/switch/navigate
108
+ - sidebar tab list
109
+ - URL bar navigation
110
+ - title/favicon observer
111
+ - resize handling
112
+ - per-site zoom
113
+
114
+ ## Phase 2 target
115
+
116
+ Ad-block and privacy:
117
+ - adblock-rust engine
118
+ - 10 bundled lists
119
+ - JS network-request interception
120
+ - cookie consent scriptlet
121
+ - WebRTC protection
122
+ - canvas noise
123
+ - navigation blocking
124
+ - cosmetic injection
125
+ - updater
126
+ - allowlist
127
+ - shield UI
128
+ - tab sleep
129
+ - HTTPS-first
130
+ - Stronghold password vault basics
131
+
132
+ ## Phase 3 target
133
+
134
+ Library + Board core:
135
+ - image hover overlay
136
+ - save-to-library
137
+ - library search/grid/detail
138
+ - board infinite canvas
139
+ - board item CRUD/autosave
140
+ - palette extraction
141
+ - download manager
142
+ - sessions auto-save
143
+
144
+ ## Phase 4 target
145
+
146
+ Artist features and polish:
147
+ - Quick Study Mode
148
+ - annotations
149
+ - rotate/flip/light table
150
+ - overlays
151
+ - timeline
152
+ - source profiles
153
+ - export formats
154
+ - reference sheets
155
+ - session manager UI
156
+ - command palette
157
+ - permissions/cert/offline/print/DoH
158
+
159
+ ## Phase 5 target
160
+
161
+ Windows v1 shipping:
162
+ - NSIS installer
163
+ - updater
164
+ - code signing
165
+ - optional crash reporting
166
+ - performance audit
167
+
168
+ ## Current implementation note
169
+
170
+ The committed code implements the Phase 0 scope only. Phase 1+ modules are represented as directory placeholders and comments where useful, but no tab WebViews are created in Phase 0.
index.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>MUSE Alpha</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/index.tsx"></script>
11
+ </body>
12
+ </html>
package.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "musealpha",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc --noEmit && vite build",
9
+ "preview": "vite preview",
10
+ "tauri": "tauri"
11
+ },
12
+ "dependencies": {
13
+ "@tauri-apps/api": "^2.0.0",
14
+ "@tauri-apps/plugin-store": "^2.0.0",
15
+ "@tauri-apps/plugin-sql": "^2.0.0",
16
+ "@tauri-apps/plugin-opener": "^2.0.0",
17
+ "@tauri-apps/plugin-clipboard-manager": "^2.0.0",
18
+ "@tauri-apps/plugin-fs": "^2.0.0",
19
+ "solid-js": "^1.9.0"
20
+ },
21
+ "devDependencies": {
22
+ "@tauri-apps/cli": "^2.0.0",
23
+ "typescript": "~5.6.0",
24
+ "vite": "^6.0.0",
25
+ "vite-plugin-solid": "^2.11.0"
26
+ }
27
+ }
src-tauri/Cargo.toml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [package]
2
+ name = "muse"
3
+ version = "0.1.0"
4
+ description = "MUSE Alpha"
5
+ authors = ["asdf98"]
6
+ edition = "2021"
7
+
8
+ [lib]
9
+ name = "muse_lib"
10
+ crate-type = ["staticlib", "cdylib", "rlib"]
11
+
12
+ [build-dependencies]
13
+ tauri-build = { version = "2", features = [] }
14
+
15
+ [dependencies]
16
+ tauri = { version = "2", features = [] }
17
+ tauri-plugin-opener = "2"
18
+ tauri-plugin-store = "2"
19
+ tauri-plugin-fs = "2"
20
+ tauri-plugin-sql = { version = "2", features = ["sqlite"] }
21
+ tauri-plugin-clipboard-manager = "2"
22
+ tauri-plugin-stronghold = "2"
23
+ serde = { version = "1", features = ["derive"] }
24
+ serde_json = "1"
25
+
26
+ [target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
27
+ tauri-plugin-global-shortcut = "2"
28
+
29
+ [profile.dev.package.scrypt]
30
+ opt-level = 3
31
+
32
+ [features]
33
+ default = ["custom-protocol"]
34
+ custom-protocol = ["tauri/custom-protocol"]
src-tauri/build.rs ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ fn main() {
2
+ tauri_build::build()
3
+ }
src-tauri/capabilities/default.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "../gen/schemas/desktop-schema.json",
3
+ "identifier": "default",
4
+ "description": "MUSE Alpha Phase 0 default capability set",
5
+ "windows": ["main"],
6
+ "permissions": [
7
+ "core:default",
8
+ "opener:default",
9
+ "core:window:allow-start-dragging",
10
+ "core:window:allow-minimize",
11
+ "core:window:allow-toggle-maximize",
12
+ "core:window:allow-close",
13
+ "store:default",
14
+ "sql:default",
15
+ "sql:allow-execute",
16
+ "fs:default",
17
+ "clipboard-manager:allow-read-text",
18
+ "clipboard-manager:allow-write-text",
19
+ "global-shortcut:allow-register",
20
+ "global-shortcut:allow-unregister",
21
+ "global-shortcut:allow-unregister-all",
22
+ "global-shortcut:allow-is-registered",
23
+ "stronghold:default"
24
+ ]
25
+ }
src-tauri/icons/README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ Add generated Tauri icon assets here before packaging (`pnpm tauri icon`).
src-tauri/migrations/001_phase0_init.sql ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CREATE TABLE IF NOT EXISTS app_meta (
2
+ key TEXT PRIMARY KEY NOT NULL,
3
+ value TEXT NOT NULL,
4
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
5
+ );
6
+
7
+ CREATE TABLE IF NOT EXISTS settings (
8
+ key TEXT PRIMARY KEY NOT NULL,
9
+ value_json TEXT NOT NULL,
10
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
11
+ );
12
+
13
+ CREATE TABLE IF NOT EXISTS workspaces (
14
+ id TEXT PRIMARY KEY NOT NULL,
15
+ name TEXT NOT NULL,
16
+ accent TEXT NOT NULL DEFAULT '#C49A3C',
17
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
18
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
19
+ );
20
+
21
+ INSERT OR IGNORE INTO app_meta (key, value) VALUES ('schema_version', '1');
src-tauri/src/lib.rs ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mod settings;
2
+ mod state;
3
+
4
+ use tauri_plugin_sql::{Migration, MigrationKind};
5
+
6
+ #[cfg_attr(mobile, tauri::mobile_entry_point)]
7
+ pub fn run() {
8
+ tauri::Builder::default()
9
+ .plugin(tauri_plugin_opener::init())
10
+ .plugin(tauri_plugin_store::Builder::default().build())
11
+ .plugin(tauri_plugin_fs::init())
12
+ .plugin(tauri_plugin_clipboard_manager::init())
13
+ .plugin(
14
+ tauri_plugin_sql::Builder::default()
15
+ .add_migrations("sqlite:muse.db", migrations())
16
+ .build(),
17
+ )
18
+ .manage(state::AppState::default())
19
+ .invoke_handler(tauri::generate_handler![
20
+ settings::phase0_status,
21
+ ])
22
+ .setup(|app| {
23
+ #[cfg(desktop)]
24
+ app.handle().plugin(tauri_plugin_global_shortcut::Builder::new().build())?;
25
+ Ok(())
26
+ })
27
+ .run(tauri::generate_context!())
28
+ .expect("error while running Muse Alpha");
29
+ }
30
+
31
+ fn migrations() -> Vec<Migration> {
32
+ vec![Migration {
33
+ version: 1,
34
+ description: "phase0_init",
35
+ sql: include_str!("../migrations/001_phase0_init.sql"),
36
+ kind: MigrationKind::Up,
37
+ }]
38
+ }
src-tauri/src/main.rs ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
2
+
3
+ fn main() {
4
+ muse_lib::run()
5
+ }
src-tauri/src/settings.rs ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ #[tauri::command]
2
+ pub fn phase0_status() -> &'static str {
3
+ "MUSE Alpha Phase 0 shell is installed"
4
+ }
src-tauri/src/state.rs ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ #[derive(Default)]
2
+ pub struct AppState {
3
+ pub phase: &'static str,
4
+ }
src-tauri/tauri.conf.json ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://schema.tauri.app/config/2",
3
+ "productName": "MUSE Alpha",
4
+ "version": "0.1.0",
5
+ "identifier": "app.muse.alpha",
6
+ "build": {
7
+ "beforeDevCommand": "pnpm dev",
8
+ "devUrl": "http://localhost:1420",
9
+ "beforeBuildCommand": "pnpm build",
10
+ "frontendDist": "../dist"
11
+ },
12
+ "app": {
13
+ "withGlobalTauri": false,
14
+ "windows": [
15
+ {
16
+ "label": "main",
17
+ "title": "MUSE Alpha",
18
+ "width": 1280,
19
+ "height": 800,
20
+ "minWidth": 960,
21
+ "minHeight": 640,
22
+ "resizable": true,
23
+ "fullscreen": false,
24
+ "decorations": false,
25
+ "transparent": false,
26
+ "shadow": true,
27
+ "center": true,
28
+ "visible": true,
29
+ "zoomHotkeys": false
30
+ }
31
+ ],
32
+ "security": {
33
+ "csp": null
34
+ }
35
+ },
36
+ "plugins": {
37
+ "sql": {
38
+ "preload": ["sqlite:muse.db"]
39
+ }
40
+ },
41
+ "bundle": {
42
+ "active": true,
43
+ "targets": "all",
44
+ "icon": [
45
+ "icons/32x32.png",
46
+ "icons/128x128.png",
47
+ "icons/128x128@2x.png",
48
+ "icons/icon.icns",
49
+ "icons/icon.ico"
50
+ ]
51
+ }
52
+ }
src/App.tsx ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createEffect, createResource, createSignal, Match, Switch } from "solid-js";
2
+ import { loadSettings, saveSettings, DEFAULT_SETTINGS } from "./store/settings";
3
+ import type { MuseSettings, ThemeName } from "./types";
4
+ import Titlebar from "./components/Titlebar";
5
+ import Sidebar from "./components/Sidebar";
6
+ import HomePage from "./components/HomePage";
7
+ import Onboarding from "./components/Onboarding";
8
+ import PlaceholderPanel from "./components/PlaceholderPanel";
9
+ import ThemeSwitcher from "./components/ThemeSwitcher";
10
+
11
+ function App() {
12
+ const [initial] = createResource(loadSettings);
13
+ const [settings, setSettings] = createSignal<MuseSettings>(DEFAULT_SETTINGS);
14
+
15
+ createEffect(() => {
16
+ const loaded = initial();
17
+ if (loaded) setSettings(loaded);
18
+ });
19
+
20
+ createEffect(() => {
21
+ document.documentElement.dataset.theme = settings().theme;
22
+ });
23
+
24
+ const updateSettings = (patch: Partial<MuseSettings>) => {
25
+ const next = { ...settings(), ...patch };
26
+ setSettings(next);
27
+ void saveSettings(next);
28
+ };
29
+
30
+ const completeOnboarding = (userName: string, workspaceName: string, theme: ThemeName) => {
31
+ updateSettings({ userName, workspaceName, theme, onboardingComplete: true, activeSection: "home" });
32
+ };
33
+
34
+ return (
35
+ <Switch>
36
+ <Match when={!settings().onboardingComplete}>
37
+ <Onboarding onComplete={completeOnboarding} />
38
+ </Match>
39
+ <Match when={settings().onboardingComplete}>
40
+ <div class="app-shell">
41
+ <Titlebar workspaceName={settings().workspaceName} />
42
+ <Sidebar active={settings().activeSection} onNavigate={(activeSection) => updateSettings({ activeSection })} />
43
+ <main class="content-shell">
44
+ <Switch>
45
+ <Match when={settings().activeSection === "home"}>
46
+ <HomePage name={settings().userName} workspaceName={settings().workspaceName} />
47
+ </Match>
48
+ <Match when={settings().activeSection === "library"}>
49
+ <PlaceholderPanel title="Library" subtitle="Phase 3 will add image import, grid, tags, search, and color metadata." />
50
+ </Match>
51
+ <Match when={settings().activeSection === "board"}>
52
+ <PlaceholderPanel title="Board" subtitle="Phase 3 will add the PureRef-style infinite canvas." />
53
+ </Match>
54
+ <Match when={settings().activeSection === "settings"}>
55
+ <section class="panel settings-panel">
56
+ <div>
57
+ <p class="eyebrow">Settings</p>
58
+ <h1>Appearance</h1>
59
+ <p class="muted">Phase 0 settings persist locally with Tauri Store.</p>
60
+ </div>
61
+ <ThemeSwitcher selected={settings().theme} onSelect={(theme) => updateSettings({ theme })} />
62
+ </section>
63
+ </Match>
64
+ </Switch>
65
+ </main>
66
+ </div>
67
+ </Match>
68
+ </Switch>
69
+ );
70
+ }
71
+
72
+ export default App;
src/components/HomePage.tsx ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export default function HomePage(props: { name: string; workspaceName: string }) {
2
+ const hour = new Date().getHours();
3
+ const greeting = hour < 12 ? "Good morning" : hour < 18 ? "Good afternoon" : "Good evening";
4
+
5
+ return (
6
+ <section class="home-page">
7
+ <div class="hero-card">
8
+ <p class="eyebrow">{props.workspaceName}</p>
9
+ <h1>{greeting}, {props.name}.</h1>
10
+ <p class="muted">Muse Phase 0 is ready: shell, themes, onboarding, settings, and local database foundation.</p>
11
+ <div class="url-shell" aria-label="Static phase 0 URL bar">
12
+ <span>⌕</span>
13
+ <input disabled value="Search or enter a URL — enabled in Phase 1" />
14
+ <span class="kbd">Ctrl L</span>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="dashboard-grid">
19
+ <article class="mini-card">
20
+ <p class="eyebrow">Recent boards</p>
21
+ <h2>Empty studio</h2>
22
+ <p>Boards arrive in Phase 3.</p>
23
+ </article>
24
+ <article class="mini-card">
25
+ <p class="eyebrow">Recently saved</p>
26
+ <h2>No library items yet</h2>
27
+ <p>The Library module lands after browser/ad-block foundations.</p>
28
+ </article>
29
+ <article class="mini-card">
30
+ <p class="eyebrow">Continue browsing</p>
31
+ <h2>Tabs next</h2>
32
+ <p>Phase 1 adds real WebView tabs and URL navigation.</p>
33
+ </article>
34
+ </div>
35
+ </section>
36
+ );
37
+ }
src/components/Onboarding.tsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createSignal, Match, Switch } from "solid-js";
2
+ import type { ThemeName } from "../types";
3
+ import ThemeSwitcher from "./ThemeSwitcher";
4
+
5
+ export default function Onboarding(props: { onComplete: (name: string, workspace: string, theme: ThemeName) => void }) {
6
+ const [step, setStep] = createSignal(0);
7
+ const [name, setName] = createSignal("Artist");
8
+ const [workspace, setWorkspace] = createSignal("First Workspace");
9
+ const [theme, setTheme] = createSignal<ThemeName>("dusk");
10
+
11
+ return (
12
+ <main class="onboarding" data-theme={theme()}>
13
+ <div class="onboarding-card">
14
+ <Switch>
15
+ <Match when={step() === 0}>
16
+ <p class="eyebrow">Welcome to Muse</p>
17
+ <h1>One quiet studio for visual research.</h1>
18
+ <p class="muted">No account. No feed. Local-first by design.</p>
19
+ <button class="primary" onClick={() => setStep(1)}>Begin</button>
20
+ </Match>
21
+ <Match when={step() === 1}>
22
+ <p class="eyebrow">Make it yours</p>
23
+ <h1>Choose your name and atmosphere.</h1>
24
+ <label>Name<input value={name()} onInput={(e) => setName(e.currentTarget.value)} /></label>
25
+ <ThemeSwitcher selected={theme()} onSelect={setTheme} />
26
+ <button class="primary" onClick={() => setStep(2)}>Next</button>
27
+ </Match>
28
+ <Match when={step() === 2}>
29
+ <p class="eyebrow">First workspace</p>
30
+ <h1>Name the project room.</h1>
31
+ <label>Workspace<input value={workspace()} onInput={(e) => setWorkspace(e.currentTarget.value)} /></label>
32
+ <button class="primary" onClick={() => props.onComplete(name(), workspace(), theme())}>Enter Muse</button>
33
+ </Match>
34
+ </Switch>
35
+ </div>
36
+ </main>
37
+ );
38
+ }
src/components/PlaceholderPanel.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ export default function PlaceholderPanel(props: { title: string; subtitle: string }) {
2
+ return (
3
+ <section class="panel placeholder-panel">
4
+ <p class="eyebrow">Phase placeholder</p>
5
+ <h1>{props.title}</h1>
6
+ <p class="muted">{props.subtitle}</p>
7
+ </section>
8
+ );
9
+ }
src/components/Sidebar.tsx ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { MuseSettings } from "../types";
2
+
3
+ type Section = MuseSettings["activeSection"];
4
+
5
+ const items: Array<{ id: Section; icon: string; label: string }> = [
6
+ { id: "home", icon: "⌂", label: "Home" },
7
+ { id: "library", icon: "▦", label: "Library" },
8
+ { id: "board", icon: "✣", label: "Board" },
9
+ { id: "settings", icon: "⚙", label: "Settings" }
10
+ ];
11
+
12
+ export default function Sidebar(props: { active: Section; onNavigate: (section: Section) => void }) {
13
+ return (
14
+ <aside class="sidebar">
15
+ <nav>
16
+ {items.map((item) => (
17
+ <button
18
+ type="button"
19
+ classList={{ "nav-item": true, active: props.active === item.id }}
20
+ onClick={() => props.onNavigate(item.id)}
21
+ title={item.label}
22
+ >
23
+ <span class="nav-icon">{item.icon}</span>
24
+ <span class="nav-label">{item.label}</span>
25
+ </button>
26
+ ))}
27
+ </nav>
28
+ <div class="sidebar-footer">
29
+ <span class="shield-dot" />
30
+ <span class="footer-label">Shield ready</span>
31
+ </div>
32
+ </aside>
33
+ );
34
+ }
src/components/ThemeSwitcher.tsx ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { ThemeName } from "../types";
2
+
3
+ const themes: Array<{ id: ThemeName; label: string }> = [
4
+ { id: "dusk", label: "Dusk" },
5
+ { id: "parchment", label: "Parchment" },
6
+ { id: "midnight", label: "Midnight" },
7
+ { id: "studio", label: "Studio" },
8
+ { id: "moss", label: "Moss" },
9
+ { id: "rose", label: "Rose" },
10
+ { id: "obsidian", label: "Obsidian" },
11
+ { id: "linen", label: "Linen" }
12
+ ];
13
+
14
+ export default function ThemeSwitcher(props: { selected: ThemeName; onSelect: (theme: ThemeName) => void }) {
15
+ return (
16
+ <div class="theme-grid">
17
+ {themes.map((theme) => (
18
+ <button
19
+ type="button"
20
+ classList={{ "theme-tile": true, selected: props.selected === theme.id }}
21
+ data-preview-theme={theme.id}
22
+ onClick={() => props.onSelect(theme.id)}
23
+ >
24
+ <span class="theme-swatch" />
25
+ {theme.label}
26
+ </button>
27
+ ))}
28
+ </div>
29
+ );
30
+ }
src/components/Titlebar.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { getCurrentWindow } from "@tauri-apps/api/window";
2
+
3
+ const appWindow = getCurrentWindow();
4
+
5
+ export default function Titlebar(props: { workspaceName: string }) {
6
+ return (
7
+ <header class="titlebar" data-tauri-drag-region>
8
+ <div class="workspace-dots" data-tauri-drag-region>
9
+ <span class="dot active" />
10
+ <span class="dot" />
11
+ <span class="dot" />
12
+ </div>
13
+ <div class="titlebar-copy" data-tauri-drag-region>
14
+ <strong>MUSE</strong>
15
+ <span>{props.workspaceName}</span>
16
+ </div>
17
+ <div class="window-controls" data-tauri-drag-region="false">
18
+ <button type="button" aria-label="Minimize" onClick={() => appWindow.minimize()}>—</button>
19
+ <button type="button" aria-label="Maximize" onClick={() => appWindow.toggleMaximize()}>□</button>
20
+ <button type="button" class="close" aria-label="Close" onClick={() => appWindow.close()}>×</button>
21
+ </div>
22
+ </header>
23
+ );
24
+ }
src/index.tsx ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ /* @refresh reload */
2
+ import { render } from "solid-js/web";
3
+ import App from "./App";
4
+ import "./styles/tokens.css";
5
+ import "./styles/app.css";
6
+
7
+ render(() => <App />, document.getElementById("root") as HTMLElement);
src/store/settings.ts ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Store } from "@tauri-apps/plugin-store";
2
+ import type { MuseSettings, ThemeName } from "../types";
3
+
4
+ export const DEFAULT_SETTINGS: MuseSettings = {
5
+ onboardingComplete: false,
6
+ userName: "Artist",
7
+ workspaceName: "First Workspace",
8
+ theme: "dusk",
9
+ activeSection: "home"
10
+ };
11
+
12
+ let storePromise: Promise<Store> | undefined;
13
+
14
+ export function settingsStore() {
15
+ if (!storePromise) {
16
+ storePromise = Store.load("settings.json", {
17
+ defaults: DEFAULT_SETTINGS,
18
+ autoSave: 250
19
+ });
20
+ }
21
+ return storePromise;
22
+ }
23
+
24
+ export async function loadSettings(): Promise<MuseSettings> {
25
+ const store = await settingsStore();
26
+ return {
27
+ onboardingComplete: (await store.get<boolean>("onboardingComplete")) ?? DEFAULT_SETTINGS.onboardingComplete,
28
+ userName: (await store.get<string>("userName")) ?? DEFAULT_SETTINGS.userName,
29
+ workspaceName: (await store.get<string>("workspaceName")) ?? DEFAULT_SETTINGS.workspaceName,
30
+ theme: ((await store.get<ThemeName>("theme")) ?? DEFAULT_SETTINGS.theme),
31
+ activeSection: ((await store.get<MuseSettings["activeSection"]>("activeSection")) ?? DEFAULT_SETTINGS.activeSection)
32
+ };
33
+ }
34
+
35
+ export async function saveSettings(settings: MuseSettings) {
36
+ const store = await settingsStore();
37
+ await Promise.all([
38
+ store.set("onboardingComplete", settings.onboardingComplete),
39
+ store.set("userName", settings.userName),
40
+ store.set("workspaceName", settings.workspaceName),
41
+ store.set("theme", settings.theme),
42
+ store.set("activeSection", settings.activeSection)
43
+ ]);
44
+ await store.save();
45
+ }
src/styles/app.css ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * { box-sizing: border-box; }
2
+ html, body, #root { width: 100%; height: 100%; margin: 0; overflow: hidden; }
3
+ body { background: var(--bg-base); color: var(--text-primary); }
4
+ button, input { font: inherit; }
5
+ button { color: inherit; }
6
+
7
+ .app-shell {
8
+ width: 100vw; height: 100vh;
9
+ display: grid;
10
+ grid-template-rows: var(--titlebar-h) 1fr;
11
+ grid-template-columns: var(--sidebar-w) 1fr;
12
+ background: radial-gradient(circle at 70% 10%, var(--accent-glow), transparent 34%), var(--bg-base);
13
+ }
14
+
15
+ .titlebar { grid-column: 1 / -1; height: var(--titlebar-h); display: flex; align-items: center; gap: 14px; padding: 0 10px; background: var(--bg-overlay); border-bottom: 1px solid var(--border); user-select: none; app-region: drag; }
16
+ [data-tauri-drag-region] { app-region: drag; }
17
+ [data-tauri-drag-region="false"], button, input { app-region: no-drag; }
18
+ .workspace-dots { display: flex; gap: 7px; }
19
+ .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--border); display: inline-block; }
20
+ .dot.active { background: var(--accent); box-shadow: 0 0 16px var(--accent-glow); }
21
+ .titlebar-copy { display: flex; align-items: center; gap: 10px; flex: 1; color: var(--text-secondary); font-size: 12px; }
22
+ .titlebar-copy strong { color: var(--text-primary); letter-spacing: .18em; }
23
+ .window-controls { display: flex; gap: 2px; }
24
+ .window-controls button { width: 34px; height: 26px; border: 0; border-radius: 6px; background: transparent; cursor: pointer; }
25
+ .window-controls button:hover { background: var(--bg-raised); }
26
+ .window-controls .close:hover { background: var(--error); color: white; }
27
+
28
+ .sidebar { grid-column: 1; grid-row: 2; width: var(--sidebar-w); background: color-mix(in srgb, var(--bg-surface) 88%, transparent); border-right: 1px solid var(--border); display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; transition: width var(--transition-slow); z-index: 10; }
29
+ .sidebar:hover { width: var(--sidebar-w-expanded); }
30
+ .sidebar nav { display: flex; flex-direction: column; gap: 8px; padding: 12px 8px; }
31
+ .nav-item { height: 40px; display: flex; align-items: center; gap: 12px; border: 0; border-radius: 10px; background: transparent; cursor: pointer; padding: 0 12px; white-space: nowrap; }
32
+ .nav-item:hover, .nav-item.active { background: var(--bg-raised); }
33
+ .nav-item.active { color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent-glow); }
34
+ .nav-icon { min-width: 16px; text-align: center; }
35
+ .nav-label, .footer-label { opacity: 0; transition: opacity var(--transition); }
36
+ .sidebar:hover .nav-label, .sidebar:hover .footer-label { opacity: 1; }
37
+ .sidebar-footer { display: flex; align-items: center; gap: 10px; padding: 12px 16px; color: var(--text-muted); font-size: 12px; }
38
+ .shield-dot { width: 9px; height: 9px; background: var(--success); border-radius: 999px; box-shadow: 0 0 10px color-mix(in srgb, var(--success) 60%, transparent); }
39
+
40
+ .content-shell { grid-column: 2; grid-row: 2; min-width: 0; overflow: auto; padding: 40px; }
41
+ .home-page { max-width: 1120px; margin: 0 auto; }
42
+ .hero-card, .panel, .mini-card, .onboarding-card { background: var(--bg-overlay); border: 1px solid var(--border); border-radius: 24px; box-shadow: 0 24px 80px rgba(0,0,0,.22); backdrop-filter: blur(20px); }
43
+ .hero-card { padding: 48px; }
44
+ .eyebrow { color: var(--accent); text-transform: uppercase; letter-spacing: .14em; font-size: 12px; font-weight: 700; margin: 0 0 12px; }
45
+ h1 { font-size: clamp(34px, 5vw, 68px); line-height: .96; margin: 0 0 18px; letter-spacing: -0.055em; }
46
+ h2 { margin: 4px 0 8px; }
47
+ .muted, .mini-card p, .placeholder-panel p { color: var(--text-secondary); }
48
+ .url-shell { margin-top: 34px; height: 54px; display: flex; align-items: center; gap: 12px; padding: 0 16px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-surface); }
49
+ .url-shell input { flex: 1; border: 0; background: transparent; color: var(--text-secondary); outline: 0; }
50
+ .kbd { border: 1px solid var(--border); border-radius: 7px; padding: 3px 7px; color: var(--text-muted); font-size: 12px; }
51
+ .dashboard-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin-top: 18px; }
52
+ .mini-card { padding: 24px; }
53
+ .panel { padding: 40px; min-height: calc(100vh - 116px); }
54
+ .settings-panel { display: flex; flex-direction: column; gap: 28px; }
55
+
56
+ .onboarding { width: 100vw; height: 100vh; display: grid; place-items: center; padding: 24px; background: radial-gradient(circle at top right, var(--accent-glow), transparent 36%), var(--bg-base); color: var(--text-primary); }
57
+ .onboarding-card { width: min(720px, 100%); padding: 48px; }
58
+ .onboarding label { display: grid; gap: 8px; margin: 20px 0; color: var(--text-secondary); }
59
+ .onboarding input { width: 100%; height: 46px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg-surface); color: var(--text-primary); padding: 0 14px; }
60
+ .primary { margin-top: 24px; height: 44px; padding: 0 20px; border: 0; border-radius: 999px; background: var(--accent); color: var(--bg-base); font-weight: 800; cursor: pointer; }
61
+ .theme-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; }
62
+ .theme-tile { display: flex; align-items: center; gap: 10px; padding: 12px; border-radius: 14px; border: 1px solid var(--border); background: var(--bg-surface); cursor: pointer; text-align: left; }
63
+ .theme-tile.selected { outline: 2px solid var(--accent); }
64
+ .theme-swatch { width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), var(--bg-raised)); border: 1px solid var(--border); }
65
+
66
+ @media (max-width: 860px) { .content-shell { padding: 20px; } .dashboard-grid { grid-template-columns: 1fr; } .hero-card { padding: 30px; } }
src/styles/tokens.css ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ font-family: Geist, Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
3
+ color: var(--text-primary);
4
+ background: var(--bg-base);
5
+ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-12: 48px;
6
+ --radius-sm: 4px; --radius: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px;
7
+ --titlebar-h: 36px; --sidebar-w: 56px; --sidebar-w-expanded: 240px;
8
+ --transition-fast: 80ms ease-out; --transition: 150ms ease-out; --transition-slow: 250ms ease-out;
9
+ }
10
+
11
+ :root[data-theme="dusk"], [data-theme="dusk"] { --bg-base:#100E0B; --bg-surface:#1A1712; --bg-raised:#232019; --bg-overlay:rgba(10,8,6,.82); --border:#3A3628; --text-primary:#F0EDE6; --text-secondary:#9A9488; --text-muted:#5A5450; --accent:#C49A3C; --accent-glow:rgba(196,154,60,.18); --success:#4CAF6E; --error:#E05252; }
12
+ :root[data-theme="parchment"], [data-theme="parchment"] { --bg-base:#F4EBDD; --bg-surface:#FFF8EA; --bg-raised:#F9F0DF; --bg-overlay:rgba(255,248,234,.86); --border:#D9C7A8; --text-primary:#2A2118; --text-secondary:#695A48; --text-muted:#9D8C76; --accent:#A36B2C; --accent-glow:rgba(163,107,44,.18); --success:#477A4A; --error:#B64C45; }
13
+ :root[data-theme="midnight"], [data-theme="midnight"] { --bg-base:#090D18; --bg-surface:#111827; --bg-raised:#172033; --bg-overlay:rgba(9,13,24,.84); --border:#27334D; --text-primary:#EEF3FF; --text-secondary:#9CA9C7; --text-muted:#586176; --accent:#8C7CF6; --accent-glow:rgba(140,124,246,.22); --success:#56C28B; --error:#F07178; }
14
+ :root[data-theme="studio"], [data-theme="studio"] { --bg-base:#F7F8FA; --bg-surface:#FFFFFF; --bg-raised:#EEF1F5; --bg-overlay:rgba(255,255,255,.88); --border:#D7DDE5; --text-primary:#151922; --text-secondary:#5E6978; --text-muted:#98A1AD; --accent:#2F6FED; --accent-glow:rgba(47,111,237,.18); --success:#1E8E5A; --error:#C83E3E; }
15
+ :root[data-theme="moss"], [data-theme="moss"] { --bg-base:#0D1410; --bg-surface:#162018; --bg-raised:#1E2A20; --bg-overlay:rgba(13,20,16,.84); --border:#304134; --text-primary:#EAF2E8; --text-secondary:#A3B39E; --text-muted:#60705D; --accent:#87A96B; --accent-glow:rgba(135,169,107,.2); --success:#82C785; --error:#D85E5E; }
16
+ :root[data-theme="rose"], [data-theme="rose"] { --bg-base:#FFF4F6; --bg-surface:#FFFFFF; --bg-raised:#FBE5EA; --bg-overlay:rgba(255,244,246,.88); --border:#EAC3CC; --text-primary:#2B1820; --text-secondary:#785B65; --text-muted:#AC8994; --accent:#C86B85; --accent-glow:rgba(200,107,133,.18); --success:#4F936A; --error:#C64D62; }
17
+ :root[data-theme="obsidian"], [data-theme="obsidian"] { --bg-base:#050606; --bg-surface:#0B1010; --bg-raised:#111818; --bg-overlay:rgba(5,6,6,.88); --border:#1D2A2A; --text-primary:#EAFDFC; --text-secondary:#8DA5A4; --text-muted:#4A5A5A; --accent:#2ED3C6; --accent-glow:rgba(46,211,198,.18); --success:#42C985; --error:#EF5555; }
18
+ :root[data-theme="linen"], [data-theme="linen"] { --bg-base:#F8EFE4; --bg-surface:#FFF9F0; --bg-raised:#F1DFCC; --bg-overlay:rgba(255,249,240,.88); --border:#D9BFA2; --text-primary:#2D2119; --text-secondary:#6F5A49; --text-muted:#A58D78; --accent:#C46F45; --accent-glow:rgba(196,111,69,.18); --success:#548B5C; --error:#BC4F45; }
src/types.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export type ThemeName =
2
+ | "dusk"
3
+ | "parchment"
4
+ | "midnight"
5
+ | "studio"
6
+ | "moss"
7
+ | "rose"
8
+ | "obsidian"
9
+ | "linen";
10
+
11
+ export interface MuseSettings {
12
+ onboardingComplete: boolean;
13
+ userName: string;
14
+ workspaceName: string;
15
+ theme: ThemeName;
16
+ activeSection: "home" | "library" | "board" | "settings";
17
+ }
tsconfig.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "allowJs": false,
8
+ "skipLibCheck": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "strict": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "moduleResolution": "Node",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "preserve",
18
+ "jsxImportSource": "solid-js"
19
+ },
20
+ "include": ["src"],
21
+ "references": []
22
+ }
vite.config.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from "vite";
2
+ import solid from "vite-plugin-solid";
3
+ // @ts-expect-error process is available in Vite config runtime
4
+ import process from "node:process";
5
+
6
+ const host = process.env.TAURI_DEV_HOST;
7
+
8
+ export default defineConfig(() => ({
9
+ plugins: [solid()],
10
+ clearScreen: false,
11
+ server: {
12
+ port: 1420,
13
+ strictPort: true,
14
+ host: host || false,
15
+ hmr: host ? { protocol: "ws", host, port: 1421 } : undefined,
16
+ watch: { ignored: ["**/src-tauri/**"] }
17
+ }
18
+ }));