synthos 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/default-pages/elevenlabs_effects_studio/chat-history.json +1 -0
- package/default-pages/elevenlabs_effects_studio/page.html +1345 -1363
- package/default-pages/elevenlabs_effects_studio/page.json +13 -11
- package/default-pages/elevenlabs_voice_studio/chat-history.json +1 -0
- package/default-pages/elevenlabs_voice_studio/page.html +782 -801
- package/default-pages/elevenlabs_voice_studio/page.json +13 -11
- package/default-pages/json_tools/chat-history.json +1 -0
- package/default-pages/json_tools/page.html +70 -90
- package/default-pages/json_tools/page.json +12 -10
- package/default-pages/my_notes/chat-history.json +1 -0
- package/default-pages/my_notes/page.html +115 -131
- package/default-pages/my_notes/page.json +14 -12
- package/default-pages/neon_asteroids/chat-history.json +1 -0
- package/default-pages/neon_asteroids/page.html +1777 -1803
- package/default-pages/neon_asteroids/page.json +14 -12
- package/default-pages/oregon_trail/chat-history.json +1 -0
- package/default-pages/oregon_trail/page.html +290 -307
- package/default-pages/oregon_trail/page.json +14 -12
- package/default-pages/solar_explorer/chat-history.json +1 -0
- package/default-pages/solar_explorer/page.html +1929 -1951
- package/default-pages/solar_explorer/page.json +14 -12
- package/default-pages/solar_tutorial/chat-history.json +1 -0
- package/default-pages/solar_tutorial/page.html +464 -478
- package/default-pages/solar_tutorial/page.json +12 -10
- package/default-pages/us_map/chat-history.json +1 -0
- package/default-pages/us_map/page.html +170 -193
- package/default-pages/us_map/page.json +14 -12
- package/default-pages/us_map/page.light.png +0 -0
- package/default-pages/us_map_1850/chat-history.json +1 -0
- package/default-pages/us_map_1850/page.html +302 -326
- package/default-pages/us_map_1850/page.json +14 -12
- package/default-pages/western_cities_1850/chat-history.json +1 -0
- package/default-pages/western_cities_1850/page.html +503 -527
- package/default-pages/western_cities_1850/page.json +14 -12
- package/default-themes/aurora-dawn.v3.css +15 -14
- package/default-themes/aurora-dusk.v3.css +26 -26
- package/default-themes/cosmos-dawn.v3.css +15 -14
- package/default-themes/cosmos-dusk.v3.css +26 -26
- package/default-themes/elemental-dawn.v3.css +200 -0
- package/default-themes/nebula-dawn.v3.css +15 -14
- package/default-themes/nebula-dusk.v3.css +24 -24
- package/default-themes/solar-flare-dawn.v3.css +15 -14
- package/default-themes/solar-flare-dusk.v3.css +26 -26
- package/dist/builders/anthropic.d.ts +26 -2
- package/dist/builders/anthropic.d.ts.map +1 -1
- package/dist/builders/anthropic.js +132 -31
- package/dist/builders/anthropic.js.map +1 -1
- package/dist/builders/claudecode.d.ts +13 -0
- package/dist/builders/claudecode.d.ts.map +1 -0
- package/dist/builders/claudecode.js +253 -0
- package/dist/builders/claudecode.js.map +1 -0
- package/dist/builders/index.d.ts +2 -1
- package/dist/builders/index.d.ts.map +1 -1
- package/dist/builders/index.js +8 -1
- package/dist/builders/index.js.map +1 -1
- package/dist/builders/openai.js +2 -1
- package/dist/builders/openai.js.map +1 -1
- package/dist/builders/types.d.ts +31 -7
- package/dist/builders/types.d.ts.map +1 -1
- package/dist/builders/types.js +60 -28
- package/dist/builders/types.js.map +1 -1
- package/dist/connectors/types.d.ts +8 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +13 -6
- package/dist/init.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +161 -14
- package/dist/migrations.js.map +1 -1
- package/dist/models/anthropic.d.ts +1 -0
- package/dist/models/anthropic.d.ts.map +1 -1
- package/dist/models/anthropic.js +129 -29
- package/dist/models/anthropic.js.map +1 -1
- package/dist/models/chainOfThought.d.ts.map +1 -1
- package/dist/models/chainOfThought.js +32 -19
- package/dist/models/chainOfThought.js.map +1 -1
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/providers.d.ts +1 -0
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/providers.js +12 -4
- package/dist/models/providers.js.map +1 -1
- package/dist/models/types.d.ts +15 -1
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js.map +1 -1
- package/dist/pages.d.ts +57 -8
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +258 -45
- package/dist/pages.js.map +1 -1
- package/dist/service/createCompletePrompt.d.ts.map +1 -1
- package/dist/service/createCompletePrompt.js +5 -0
- package/dist/service/createCompletePrompt.js.map +1 -1
- package/dist/service/mediaCache.d.ts +36 -0
- package/dist/service/mediaCache.d.ts.map +1 -0
- package/dist/service/mediaCache.js +182 -0
- package/dist/service/mediaCache.js.map +1 -0
- package/dist/service/pageValidator.d.ts +25 -0
- package/dist/service/pageValidator.d.ts.map +1 -0
- package/dist/service/pageValidator.js +315 -0
- package/dist/service/pageValidator.js.map +1 -0
- package/dist/service/server.d.ts.map +1 -1
- package/dist/service/server.js +4 -0
- package/dist/service/server.js.map +1 -1
- package/dist/service/sharedTableSchema.d.ts +73 -0
- package/dist/service/sharedTableSchema.d.ts.map +1 -0
- package/dist/service/sharedTableSchema.js +206 -0
- package/dist/service/sharedTableSchema.js.map +1 -0
- package/dist/service/transformPage.d.ts +49 -11
- package/dist/service/transformPage.d.ts.map +1 -1
- package/dist/service/transformPage.js +354 -241
- package/dist/service/transformPage.js.map +1 -1
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +285 -34
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.d.ts.map +1 -1
- package/dist/service/useConnectorRoutes.js +170 -32
- package/dist/service/useConnectorRoutes.js.map +1 -1
- package/dist/service/useDataRoutes.d.ts.map +1 -1
- package/dist/service/useDataRoutes.js +59 -2
- package/dist/service/useDataRoutes.js.map +1 -1
- package/dist/service/useExtractRoutes.d.ts +4 -0
- package/dist/service/useExtractRoutes.d.ts.map +1 -0
- package/dist/service/useExtractRoutes.js +304 -0
- package/dist/service/useExtractRoutes.js.map +1 -0
- package/dist/service/usePageRoutes.d.ts +17 -0
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +1388 -483
- package/dist/service/usePageRoutes.js.map +1 -1
- package/dist/service/useSharedDataRoutes.d.ts.map +1 -1
- package/dist/service/useSharedDataRoutes.js +54 -2
- package/dist/service/useSharedDataRoutes.js.map +1 -1
- package/dist/settings.d.ts +27 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +40 -1
- package/dist/settings.js.map +1 -1
- package/dist/themes.d.ts +0 -5
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +3 -95
- package/dist/themes.js.map +1 -1
- package/migration-rules/v2-to-v3.md +277 -119
- package/package.json +5 -1
- package/{default-pages/application → required-pages/_shell}/page.html +56 -42
- package/required-pages/_shell/page.json +14 -0
- package/required-pages/_starters/page.html +534 -0
- package/required-pages/_starters/page.json +12 -0
- package/required-pages/builder/page.html +353 -43
- package/required-pages/builder/page.json +12 -10
- package/required-pages/pages/page.html +697 -924
- package/required-pages/pages/page.json +12 -10
- package/required-pages/settings/page.html +1888 -1753
- package/required-pages/settings/page.json +12 -10
- package/required-pages/synthos_apis/page.html +834 -845
- package/required-pages/synthos_apis/page.json +12 -10
- package/required-pages/synthos_scripts/page.html +74 -88
- package/required-pages/synthos_scripts/page.json +12 -10
- package/scripts/append-instructions.py +90 -0
- package/scripts/audit-instructions.py +76 -0
- package/scripts/cleanup-shell-markup.mjs +112 -0
- package/service-connectors/buffer/connector.json +46 -0
- package/service-connectors/canva/connector.json +67 -0
- package/service-connectors/elevenlabs/connector.json +1 -1
- package/src/builders/anthropic.ts +150 -25
- package/src/builders/claudecode.ts +310 -0
- package/src/builders/index.ts +7 -1
- package/src/builders/openai.ts +2 -1
- package/src/builders/types.ts +93 -32
- package/src/connectors/types.ts +8 -0
- package/src/init.ts +13 -7
- package/src/migrations.ts +187 -16
- package/src/models/anthropic.ts +140 -30
- package/src/models/chainOfThought.ts +33 -18
- package/src/models/index.ts +2 -2
- package/src/models/providers.ts +10 -1
- package/src/models/types.ts +21 -1
- package/src/pages.ts +271 -35
- package/src/service/createCompletePrompt.ts +6 -0
- package/src/service/mediaCache.ts +206 -0
- package/src/service/pageValidator.ts +337 -0
- package/src/service/server.ts +4 -0
- package/src/service/sharedTableSchema.ts +236 -0
- package/src/service/transformPage.ts +370 -260
- package/src/service/useApiRoutes.ts +283 -32
- package/src/service/useConnectorRoutes.ts +189 -34
- package/src/service/useDataRoutes.ts +198 -116
- package/src/service/useExtractRoutes.ts +331 -0
- package/src/service/usePageRoutes.ts +1414 -394
- package/src/service/useSharedDataRoutes.ts +184 -109
- package/src/settings.ts +65 -0
- package/src/themes.ts +78 -180
- package/starters/blank_starter/chat-history.json +1 -0
- package/starters/blank_starter/page.dark.png +0 -0
- package/starters/blank_starter/page.html +47 -0
- package/starters/blank_starter/page.json +13 -0
- package/starters/blank_starter/page.light.png +0 -0
- package/starters/calculator_starter/chat-history.json +1 -0
- package/starters/calculator_starter/page.dark.png +0 -0
- package/starters/calculator_starter/page.html +232 -0
- package/starters/calculator_starter/page.json +13 -0
- package/starters/calculator_starter/page.light.png +0 -0
- package/starters/calendar_starter/chat-history.json +1 -0
- package/starters/calendar_starter/page.dark.png +0 -0
- package/starters/calendar_starter/page.html +495 -0
- package/starters/calendar_starter/page.json +13 -0
- package/starters/calendar_starter/page.light.png +0 -0
- package/starters/chat_starter/chat-history.json +1 -0
- package/starters/chat_starter/page.dark.png +0 -0
- package/starters/chat_starter/page.html +351 -0
- package/starters/chat_starter/page.json +13 -0
- package/starters/chat_starter/page.light.png +0 -0
- package/starters/checklist_starter/chat-history.json +1 -0
- package/starters/checklist_starter/page.dark.png +0 -0
- package/starters/checklist_starter/page.html +437 -0
- package/starters/checklist_starter/page.json +13 -0
- package/starters/checklist_starter/page.light.png +0 -0
- package/starters/dashboard_starter/chat-history.json +1 -0
- package/starters/dashboard_starter/page.dark.png +0 -0
- package/starters/dashboard_starter/page.html +195 -0
- package/starters/dashboard_starter/page.json +13 -0
- package/starters/dashboard_starter/page.light.png +0 -0
- package/starters/form_starter/chat-history.json +1 -0
- package/starters/form_starter/page.dark.png +0 -0
- package/starters/form_starter/page.html +313 -0
- package/starters/form_starter/page.json +13 -0
- package/starters/form_starter/page.light.png +0 -0
- package/starters/gallery_starter/chat-history.json +1 -0
- package/starters/gallery_starter/page.dark.png +0 -0
- package/starters/gallery_starter/page.html +418 -0
- package/starters/gallery_starter/page.json +13 -0
- package/starters/gallery_starter/page.light.png +0 -0
- package/starters/generator_starter/chat-history.json +1 -0
- package/starters/generator_starter/page.dark.png +0 -0
- package/starters/generator_starter/page.html +261 -0
- package/starters/generator_starter/page.json +13 -0
- package/starters/generator_starter/page.light.png +0 -0
- package/starters/index.html +538 -0
- package/starters/kanban_starter/chat-history.json +1 -0
- package/starters/kanban_starter/page.dark.png +0 -0
- package/starters/kanban_starter/page.html +432 -0
- package/starters/kanban_starter/page.json +13 -0
- package/starters/kanban_starter/page.light.png +0 -0
- package/starters/presentation_builder/chat-history.json +1 -0
- package/starters/presentation_builder/page.dark.png +0 -0
- package/starters/presentation_builder/page.html +970 -0
- package/starters/presentation_builder/page.json +15 -0
- package/starters/presentation_builder/page.light.png +0 -0
- package/starters/presentation_builder/presentation_voice/voice_config.json +9 -0
- package/starters/pulse_starter/chat-history.json +1 -0
- package/starters/pulse_starter/page.dark.png +0 -0
- package/starters/pulse_starter/page.html +698 -0
- package/starters/pulse_starter/page.json +13 -0
- package/starters/pulse_starter/page.light.png +0 -0
- package/starters/quiz_starter/chat-history.json +1 -0
- package/starters/quiz_starter/page.dark.png +0 -0
- package/starters/quiz_starter/page.html +292 -0
- package/starters/quiz_starter/page.json +13 -0
- package/starters/quiz_starter/page.light.png +0 -0
- package/starters/reference_starter/chat-history.json +1 -0
- package/starters/reference_starter/page.dark.png +0 -0
- package/starters/reference_starter/page.html +250 -0
- package/starters/reference_starter/page.json +13 -0
- package/starters/reference_starter/page.light.png +0 -0
- package/starters/retro_game_starter/chat-history.json +1 -0
- package/starters/retro_game_starter/page.dark.png +0 -0
- package/{default-pages → starters}/retro_game_starter/page.html +1281 -1308
- package/starters/retro_game_starter/page.json +15 -0
- package/starters/retro_game_starter/page.light.png +0 -0
- package/starters/roster_starter/chat-history.json +1 -0
- package/starters/roster_starter/page.dark.png +0 -0
- package/starters/roster_starter/page.html +600 -0
- package/starters/roster_starter/page.json +13 -0
- package/starters/roster_starter/page.light.png +0 -0
- package/starters/server.js +182 -0
- package/starters/start.cmd +1 -0
- package/starters/timeline_starter/chat-history.json +1 -0
- package/starters/timeline_starter/page.dark.png +0 -0
- package/starters/timeline_starter/page.html +446 -0
- package/starters/timeline_starter/page.json +13 -0
- package/starters/timeline_starter/page.light.png +0 -0
- package/starters/tutorial_starter/chat-history.json +1 -0
- package/starters/tutorial_starter/page.dark.png +0 -0
- package/starters/tutorial_starter/page.html +283 -0
- package/starters/tutorial_starter/page.json +13 -0
- package/starters/tutorial_starter/page.light.png +0 -0
- package/static-files/agent.v3.js +122 -0
- package/static-files/connector.v3.js +48 -0
- package/static-files/extract.v3.js +188 -0
- package/static-files/helpers.v3.js +50 -6
- package/static-files/page-bridge.js +114 -0
- package/static-files/page.v3.js +1292 -1290
- package/static-files/script.v3.js +32 -0
- package/static-files/server.v3.js +89 -0
- package/static-files/shell-bridge.v3.js +174 -0
- package/static-files/shell-modals.v3.js +521 -0
- package/static-files/{shell.css → shell.v3.css} +271 -22
- package/static-files/shell.v3.js +1865 -0
- package/static-files/storage.v3.js +176 -0
- package/tests/anthropic.spec.ts +42 -7
- package/tests/builders.spec.ts +70 -2
- package/tests/pageValidator.spec.ts +548 -0
- package/tests/profiles.spec.ts +122 -0
- package/tests/sharedTableSchema.spec.ts +242 -0
- package/tests/transformPage.spec.ts +62 -81
- package/default-pages/application/page.json +0 -10
- package/default-pages/retro_game_starter/page.json +0 -12
- package/default-pages/sidebar_page/page.html +0 -51
- package/default-pages/sidebar_page/page.json +0 -10
- package/default-pages/two-panel_page/page.html +0 -68
- package/default-pages/two-panel_page/page.json +0 -10
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Static file server + minimal Synthos /api shims so starter pages render
|
|
3
|
+
// out-of-shell with correct theme + script loading. No npm dependencies.
|
|
4
|
+
//
|
|
5
|
+
// node starters/server.js [port]
|
|
6
|
+
//
|
|
7
|
+
// Serves the project root, so /default-themes/, /static-files/, /starters/
|
|
8
|
+
// all resolve correctly. The active theme is stored in a `starters_theme`
|
|
9
|
+
// cookie and toggled via POST /api/set-theme — both /api/theme.css and
|
|
10
|
+
// /api/theme-info.js read from it on every request, mirroring the contract
|
|
11
|
+
// the real Synthos shell provides via src/service/useApiRoutes.ts.
|
|
12
|
+
|
|
13
|
+
const http = require('http');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const PORT = parseInt(process.argv[2] || process.env.PORT || '8080', 10);
|
|
18
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
const THEMES_DIR = path.join(ROOT, 'default-themes');
|
|
20
|
+
const DEFAULT_THEME = 'nebula-dusk';
|
|
21
|
+
|
|
22
|
+
const MIME = {
|
|
23
|
+
'.html': 'text/html; charset=utf-8',
|
|
24
|
+
'.htm': 'text/html; charset=utf-8',
|
|
25
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
26
|
+
'.mjs': 'application/javascript; charset=utf-8',
|
|
27
|
+
'.css': 'text/css; charset=utf-8',
|
|
28
|
+
'.json': 'application/json; charset=utf-8',
|
|
29
|
+
'.svg': 'image/svg+xml',
|
|
30
|
+
'.png': 'image/png',
|
|
31
|
+
'.jpg': 'image/jpeg',
|
|
32
|
+
'.jpeg': 'image/jpeg',
|
|
33
|
+
'.gif': 'image/gif',
|
|
34
|
+
'.webp': 'image/webp',
|
|
35
|
+
'.ico': 'image/x-icon',
|
|
36
|
+
'.woff': 'font/woff',
|
|
37
|
+
'.woff2': 'font/woff2',
|
|
38
|
+
'.ttf': 'font/ttf',
|
|
39
|
+
'.otf': 'font/otf',
|
|
40
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
41
|
+
'.md': 'text/plain; charset=utf-8',
|
|
42
|
+
'.llmd': 'text/plain; charset=utf-8'
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function getCookie(req, name) {
|
|
46
|
+
const header = req.headers.cookie || '';
|
|
47
|
+
const m = header.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]+)'));
|
|
48
|
+
return m ? decodeURIComponent(m[1]) : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function activeTheme(req, url) {
|
|
52
|
+
const q = url.searchParams.get('theme');
|
|
53
|
+
if (q) return q;
|
|
54
|
+
return getCookie(req, 'starters_theme') || DEFAULT_THEME;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readThemeJson(name) {
|
|
58
|
+
try { return JSON.parse(fs.readFileSync(path.join(THEMES_DIR, name + '.json'), 'utf8')); }
|
|
59
|
+
catch { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readThemeCss(name) {
|
|
63
|
+
const v3 = path.join(THEMES_DIR, name + '.v3.css');
|
|
64
|
+
if (fs.existsSync(v3)) return { css: fs.readFileSync(v3, 'utf8'), version: 3 };
|
|
65
|
+
const v2 = path.join(THEMES_DIR, name + '.v2.css');
|
|
66
|
+
if (fs.existsSync(v2)) return { css: fs.readFileSync(v2, 'utf8'), version: 2 };
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function listThemes() {
|
|
71
|
+
return fs.readdirSync(THEMES_DIR)
|
|
72
|
+
.filter(f => f.endsWith('.json'))
|
|
73
|
+
.map(f => f.replace(/\.json$/, ''))
|
|
74
|
+
.sort();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function send(res, status, body, headers) {
|
|
78
|
+
res.writeHead(status, Object.assign({ 'Cache-Control': 'no-store' }, headers || {}));
|
|
79
|
+
res.end(body);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function serveStatic(req, res, urlPath) {
|
|
83
|
+
let rel;
|
|
84
|
+
try { rel = decodeURIComponent(urlPath); } catch { rel = urlPath; }
|
|
85
|
+
if (rel === '/' || rel === '') rel = '/starters/index.html';
|
|
86
|
+
const full = path.normalize(path.join(ROOT, rel));
|
|
87
|
+
if (!full.startsWith(ROOT)) return send(res, 403, 'Forbidden');
|
|
88
|
+
fs.stat(full, (err, stat) => {
|
|
89
|
+
if (err) return send(res, 404, 'Not Found: ' + rel);
|
|
90
|
+
let target = full;
|
|
91
|
+
if (stat.isDirectory()) {
|
|
92
|
+
target = path.join(full, 'index.html');
|
|
93
|
+
if (!fs.existsSync(target)) return send(res, 404, 'No index in ' + rel);
|
|
94
|
+
}
|
|
95
|
+
const mime = MIME[path.extname(target).toLowerCase()] || 'application/octet-stream';
|
|
96
|
+
res.writeHead(200, { 'Content-Type': mime, 'Cache-Control': 'no-store' });
|
|
97
|
+
fs.createReadStream(target).pipe(res);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const server = http.createServer((req, res) => {
|
|
102
|
+
const url = new URL(req.url, 'http://localhost');
|
|
103
|
+
const p = url.pathname;
|
|
104
|
+
|
|
105
|
+
if (p === '/api/themes') {
|
|
106
|
+
return send(res, 200, JSON.stringify(listThemes()),
|
|
107
|
+
{ 'Content-Type': 'application/json; charset=utf-8' });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (p === '/api/theme.css') {
|
|
111
|
+
const name = activeTheme(req, url);
|
|
112
|
+
const loaded = readThemeCss(name);
|
|
113
|
+
if (!loaded) return send(res, 404, `/* theme not found: ${name} */`,
|
|
114
|
+
{ 'Content-Type': 'text/css; charset=utf-8' });
|
|
115
|
+
return send(res, 200, loaded.css,
|
|
116
|
+
{ 'Content-Type': 'text/css; charset=utf-8' });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (p === '/api/theme-info.js') {
|
|
120
|
+
const name = activeTheme(req, url);
|
|
121
|
+
const info = readThemeJson(name) || { mode: 'light', colors: {} };
|
|
122
|
+
const loaded = readThemeCss(name);
|
|
123
|
+
const version = loaded ? loaded.version : 1;
|
|
124
|
+
const payload = Object.assign({}, info, { name, version });
|
|
125
|
+
let js = `window.themeInfo=${JSON.stringify(payload)};`
|
|
126
|
+
+ `document.documentElement.classList.add((window.themeInfo.mode||'light')+"-mode");`;
|
|
127
|
+
if (version >= 3) {
|
|
128
|
+
js += `document.documentElement.classList.add(${JSON.stringify(name)});`;
|
|
129
|
+
}
|
|
130
|
+
js += `document.documentElement.setAttribute("data-toolbar","left");`;
|
|
131
|
+
return send(res, 200, js,
|
|
132
|
+
{ 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (p === '/api/set-theme') {
|
|
136
|
+
const finish = (theme) => {
|
|
137
|
+
if (!readThemeCss(theme)) {
|
|
138
|
+
return send(res, 400, JSON.stringify({ ok: false, error: 'Unknown theme: ' + theme }),
|
|
139
|
+
{ 'Content-Type': 'application/json; charset=utf-8' });
|
|
140
|
+
}
|
|
141
|
+
return send(res, 200, JSON.stringify({ ok: true, theme }), {
|
|
142
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
143
|
+
'Set-Cookie': `starters_theme=${encodeURIComponent(theme)}; Path=/; SameSite=Lax`
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
if (req.method === 'GET') {
|
|
147
|
+
return finish(url.searchParams.get('theme') || DEFAULT_THEME);
|
|
148
|
+
}
|
|
149
|
+
let body = '';
|
|
150
|
+
req.on('data', c => { body += c; if (body.length > 4096) req.destroy(); });
|
|
151
|
+
req.on('end', () => {
|
|
152
|
+
let theme = DEFAULT_THEME;
|
|
153
|
+
try { theme = JSON.parse(body || '{}').theme || DEFAULT_THEME; }
|
|
154
|
+
catch { return send(res, 400, 'Invalid JSON body'); }
|
|
155
|
+
finish(theme);
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (p === '/api/page-info.js') {
|
|
161
|
+
const page = url.searchParams.get('page') || '';
|
|
162
|
+
const js = `window.pageInfo=${JSON.stringify({
|
|
163
|
+
name: page, mode: 'unlocked', latestPageVersion: 3,
|
|
164
|
+
title: page, categories: ['Starters'],
|
|
165
|
+
isRequiredPage: false, productName: 'SynthOS'
|
|
166
|
+
})};`;
|
|
167
|
+
return send(res, 200, js,
|
|
168
|
+
{ 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
172
|
+
return send(res, 405, 'Method Not Allowed');
|
|
173
|
+
}
|
|
174
|
+
serveStatic(req, res, p);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
server.listen(PORT, () => {
|
|
178
|
+
console.log(`[starters] root: ${ROOT}`);
|
|
179
|
+
console.log(`[starters] open: http://localhost:${PORT}/starters/index.html`);
|
|
180
|
+
console.log(`[starters] themes: ${listThemes().join(', ')}`);
|
|
181
|
+
console.log(`[starters] default: ${DEFAULT_THEME}`);
|
|
182
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx http-server -c-1 -p 8080
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"role":"assistant","content":"Welcome to the Timeline starter — a chronological vertical rail with date-anchored event cards. Tell me what you'd like to chronicle (e.g. project history, changelog, biography, road-trip log, war-room incident timeline) and I'll wire the events, categories, and edit drawer."}]
|
|
Binary file
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - Timeline Starter</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>
|
|
8
|
+
html, body { height: 100%; }
|
|
9
|
+
body { margin: 0; }
|
|
10
|
+
.viewer-panel { height: 100%; }
|
|
11
|
+
.tl-app {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
height: 100%;
|
|
15
|
+
min-height: 100%;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
}
|
|
18
|
+
.tl-header {
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
align-items: center;
|
|
22
|
+
padding: 14px 24px;
|
|
23
|
+
border-bottom: 1px solid var(--bodyDivider, #edebe9);
|
|
24
|
+
background: var(--defaultStateBackground, #faf9f8);
|
|
25
|
+
gap: 12px;
|
|
26
|
+
flex: 0 0 auto;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
}
|
|
29
|
+
.tl-header h1 {
|
|
30
|
+
margin: 0;
|
|
31
|
+
font-size: 18px;
|
|
32
|
+
font-weight: 700;
|
|
33
|
+
color: var(--bodyText, #333333);
|
|
34
|
+
}
|
|
35
|
+
.tl-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
36
|
+
.filter-row {
|
|
37
|
+
display: flex;
|
|
38
|
+
gap: 6px;
|
|
39
|
+
align-items: center;
|
|
40
|
+
flex-wrap: wrap;
|
|
41
|
+
}
|
|
42
|
+
.filter-chip {
|
|
43
|
+
padding: 4px 10px;
|
|
44
|
+
border: 1px solid var(--inputBorder, #d2d0ce);
|
|
45
|
+
border-radius: 14px;
|
|
46
|
+
background: var(--bodyBackground, #ffffff);
|
|
47
|
+
color: var(--bodyText, #333333);
|
|
48
|
+
font-size: 12px;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
}
|
|
51
|
+
.filter-chip:hover { background: var(--bodyBackgroundHovered, #f3f2f1); }
|
|
52
|
+
.filter-chip.active {
|
|
53
|
+
background: var(--themePrimary, #0078d4);
|
|
54
|
+
border-color: var(--themePrimary, #0078d4);
|
|
55
|
+
color: #ffffff;
|
|
56
|
+
}
|
|
57
|
+
.sort-toggle {
|
|
58
|
+
padding: 4px 10px;
|
|
59
|
+
border: 1px solid var(--inputBorder, #d2d0ce);
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
background: var(--bodyBackground, #ffffff);
|
|
62
|
+
color: var(--bodyText, #333333);
|
|
63
|
+
font-size: 12px;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
}
|
|
66
|
+
.sort-toggle:hover { background: var(--bodyBackgroundHovered, #f3f2f1); }
|
|
67
|
+
|
|
68
|
+
.tl-scroll {
|
|
69
|
+
flex: 1 1 auto;
|
|
70
|
+
min-height: 0;
|
|
71
|
+
overflow-y: auto;
|
|
72
|
+
background: var(--bodyBackground, #ffffff);
|
|
73
|
+
}
|
|
74
|
+
.tl-rail {
|
|
75
|
+
position: relative;
|
|
76
|
+
padding: 24px 24px 24px 56px;
|
|
77
|
+
}
|
|
78
|
+
.tl-rail::before {
|
|
79
|
+
content: '';
|
|
80
|
+
position: absolute;
|
|
81
|
+
left: 28px;
|
|
82
|
+
top: 24px;
|
|
83
|
+
bottom: 24px;
|
|
84
|
+
width: 2px;
|
|
85
|
+
background: var(--bodyDivider, #edebe9);
|
|
86
|
+
}
|
|
87
|
+
.tl-empty {
|
|
88
|
+
text-align: center;
|
|
89
|
+
color: var(--bodySubtext, #605e5c);
|
|
90
|
+
padding: 48px 24px;
|
|
91
|
+
font-size: 13px;
|
|
92
|
+
}
|
|
93
|
+
.tl-event {
|
|
94
|
+
position: relative;
|
|
95
|
+
margin-bottom: 16px;
|
|
96
|
+
}
|
|
97
|
+
.tl-event:last-child { margin-bottom: 0; }
|
|
98
|
+
.tl-marker {
|
|
99
|
+
position: absolute;
|
|
100
|
+
left: -32px;
|
|
101
|
+
top: 14px;
|
|
102
|
+
width: 14px;
|
|
103
|
+
height: 14px;
|
|
104
|
+
border-radius: 50%;
|
|
105
|
+
background: var(--themePrimary, #0078d4);
|
|
106
|
+
border: 3px solid var(--bodyBackground, #ffffff);
|
|
107
|
+
box-shadow: 0 0 0 1px var(--bodyDivider, #edebe9);
|
|
108
|
+
z-index: 1;
|
|
109
|
+
}
|
|
110
|
+
.tl-card {
|
|
111
|
+
background: var(--defaultStateBackground, #faf9f8);
|
|
112
|
+
border: 1px solid var(--bodyDivider, #edebe9);
|
|
113
|
+
border-radius: 8px;
|
|
114
|
+
padding: 12px 16px;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: border-color 0.15s ease;
|
|
117
|
+
}
|
|
118
|
+
.tl-card:hover { border-color: var(--themePrimary, #0078d4); }
|
|
119
|
+
.tl-date {
|
|
120
|
+
font-size: 11px;
|
|
121
|
+
font-weight: 600;
|
|
122
|
+
color: var(--bodySubtext, #605e5c);
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.5px;
|
|
125
|
+
margin-bottom: 4px;
|
|
126
|
+
}
|
|
127
|
+
.tl-title {
|
|
128
|
+
font-size: 15px;
|
|
129
|
+
font-weight: 600;
|
|
130
|
+
color: var(--bodyText, #333333);
|
|
131
|
+
margin-bottom: 6px;
|
|
132
|
+
}
|
|
133
|
+
.tl-desc {
|
|
134
|
+
font-size: 13px;
|
|
135
|
+
color: var(--bodySubtext, #605e5c);
|
|
136
|
+
line-height: 1.45;
|
|
137
|
+
white-space: pre-wrap;
|
|
138
|
+
word-wrap: break-word;
|
|
139
|
+
}
|
|
140
|
+
.tl-cat {
|
|
141
|
+
display: inline-block;
|
|
142
|
+
margin-top: 8px;
|
|
143
|
+
padding: 2px 8px;
|
|
144
|
+
border-radius: 10px;
|
|
145
|
+
font-size: 10px;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
text-transform: uppercase;
|
|
148
|
+
letter-spacing: 0.4px;
|
|
149
|
+
background: var(--bodyDivider, #edebe9);
|
|
150
|
+
color: var(--bodyText, #333333);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Drawer (local — v3 themes do not inject .drawer-overlay into iframe content) */
|
|
154
|
+
.drawer-overlay {
|
|
155
|
+
display: none;
|
|
156
|
+
position: fixed;
|
|
157
|
+
inset: 0;
|
|
158
|
+
background: rgba(0, 0, 0, 0.4);
|
|
159
|
+
z-index: 100;
|
|
160
|
+
justify-content: flex-end;
|
|
161
|
+
}
|
|
162
|
+
.drawer-overlay.open { display: flex; }
|
|
163
|
+
.drawer {
|
|
164
|
+
width: 420px;
|
|
165
|
+
max-width: 90vw;
|
|
166
|
+
background: var(--bodyBackground, #ffffff);
|
|
167
|
+
box-shadow: -2px 0 12px rgba(0, 0, 0, 0.15);
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
.drawer-header {
|
|
173
|
+
padding: 16px 20px;
|
|
174
|
+
border-bottom: 1px solid var(--bodyDivider, #edebe9);
|
|
175
|
+
display: flex;
|
|
176
|
+
justify-content: space-between;
|
|
177
|
+
align-items: center;
|
|
178
|
+
}
|
|
179
|
+
.drawer-header h2 {
|
|
180
|
+
margin: 0;
|
|
181
|
+
font-size: 18px;
|
|
182
|
+
color: var(--bodyText, #333333);
|
|
183
|
+
}
|
|
184
|
+
.drawer-body {
|
|
185
|
+
flex: 1;
|
|
186
|
+
overflow-y: auto;
|
|
187
|
+
padding: 20px;
|
|
188
|
+
}
|
|
189
|
+
.drawer-footer {
|
|
190
|
+
padding: 14px 20px;
|
|
191
|
+
border-top: 1px solid var(--bodyDivider, #edebe9);
|
|
192
|
+
display: flex;
|
|
193
|
+
justify-content: space-between;
|
|
194
|
+
gap: 8px;
|
|
195
|
+
}
|
|
196
|
+
.form-group { margin-bottom: 14px; }
|
|
197
|
+
.form-label {
|
|
198
|
+
display: block;
|
|
199
|
+
font-size: 12px;
|
|
200
|
+
color: var(--bodySubtext, #605e5c);
|
|
201
|
+
margin-bottom: 4px;
|
|
202
|
+
}
|
|
203
|
+
.form-input, .form-textarea, .form-select {
|
|
204
|
+
width: 100%;
|
|
205
|
+
padding: 8px 10px;
|
|
206
|
+
border: 1px solid var(--inputBorder, #d2d0ce);
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
background: var(--bodyBackground, #ffffff);
|
|
209
|
+
color: var(--bodyText, #333333);
|
|
210
|
+
font: inherit;
|
|
211
|
+
box-sizing: border-box;
|
|
212
|
+
}
|
|
213
|
+
.form-textarea { min-height: 90px; resize: vertical; }
|
|
214
|
+
.close-btn {
|
|
215
|
+
background: transparent;
|
|
216
|
+
border: none;
|
|
217
|
+
font-size: 22px;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
color: var(--bodySubtext, #605e5c);
|
|
220
|
+
line-height: 1;
|
|
221
|
+
}
|
|
222
|
+
</style>
|
|
223
|
+
</head>
|
|
224
|
+
<body>
|
|
225
|
+
<div class="viewer-panel" id="viewerPanel" style="padding: 0;">
|
|
226
|
+
<div class="tl-app">
|
|
227
|
+
<div class="tl-header">
|
|
228
|
+
<div>
|
|
229
|
+
<h1>[Timeline Title]</h1>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="tl-actions">
|
|
232
|
+
<div class="filter-row" id="filterRow">
|
|
233
|
+
<button class="filter-chip active" data-cat="all">All</button>
|
|
234
|
+
<button class="filter-chip" data-cat="milestone">Milestone</button>
|
|
235
|
+
<button class="filter-chip" data-cat="release">Release</button>
|
|
236
|
+
<button class="filter-chip" data-cat="incident">Incident</button>
|
|
237
|
+
<button class="filter-chip" data-cat="note">Note</button>
|
|
238
|
+
</div>
|
|
239
|
+
<button class="sort-toggle" id="sortToggle" title="Sort order">Newest first</button>
|
|
240
|
+
<button class="flm-button flm-button--primary" id="addEventBtn" data-icon="Add">Add Event</button>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="tl-scroll">
|
|
244
|
+
<div class="tl-rail" id="rail"></div>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div class="drawer-overlay" id="drawerOverlay">
|
|
250
|
+
<div class="drawer">
|
|
251
|
+
<div class="drawer-header">
|
|
252
|
+
<h2 id="drawerTitle">Event</h2>
|
|
253
|
+
<button class="close-btn" id="drawerClose" aria-label="Close">×</button>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="drawer-body">
|
|
256
|
+
<div class="form-group"><label class="form-label" for="evDate">Date</label><input id="evDate" class="form-input" type="date"></div>
|
|
257
|
+
<div class="form-group"><label class="form-label" for="evTitle">Title</label><input id="evTitle" class="form-input" type="text"></div>
|
|
258
|
+
<div class="form-group"><label class="form-label" for="evDesc">Description</label><textarea id="evDesc" class="form-textarea"></textarea></div>
|
|
259
|
+
<div class="form-group"><label class="form-label" for="evCat">Category</label>
|
|
260
|
+
<select id="evCat" class="form-select">
|
|
261
|
+
<option value="milestone">Milestone</option>
|
|
262
|
+
<option value="release">Release</option>
|
|
263
|
+
<option value="incident">Incident</option>
|
|
264
|
+
<option value="note">Note</option>
|
|
265
|
+
</select>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
<div class="drawer-footer">
|
|
269
|
+
<button class="flm-button" id="deleteBtn" data-icon="Delete">Delete</button>
|
|
270
|
+
<div style="display: flex; gap: 8px;">
|
|
271
|
+
<button class="flm-button" id="cancelBtn">Cancel</button>
|
|
272
|
+
<button class="flm-button flm-button--primary" id="saveBtn" data-icon="Save">Save</button>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<div id="instructions" style="display: none;" data-locked="true">Timeline archetype (STARTERS.md §3.10). Vertical rail with date markers; cards branch right (simpler than alternating). The rail is rendered as a CSS pseudo-element on .tl-rail (::before). Filter chips narrow visible events by category. Sort toggle in the header flips between newest first and chronological. Click any card to open the edit drawer. The drawer overlay uses LOCAL .drawer-overlay CSS — v3 themes do not inject those styles into iframe content (per WISDOM iframe-modal-overlay rule). Persist events with synthos.shared.data.save("<table>", event) / .list("<table>") / .remove("<table>", id). Default fields: id, date (ISO), title, description, category. Replace [Timeline Title] and the category list with the user's domain. Use theme tokens (avoid the natural pallet) unless the user requests an alternative color.</div>
|
|
279
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
280
|
+
|
|
281
|
+
<script>
|
|
282
|
+
(function () {
|
|
283
|
+
var events = [
|
|
284
|
+
{ id: 'e1', date: '2024-01-15', title: '[Event Title]', description: '[Short description of what happened on this date.]', category: 'milestone' },
|
|
285
|
+
{ id: 'e2', date: '2024-04-08', title: '[Event Title]', description: '[Short description of what happened on this date.]', category: 'release' },
|
|
286
|
+
{ id: 'e3', date: '2024-09-22', title: '[Event Title]', description: '[Short description of what happened on this date.]', category: 'note' }
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
var activeCat = 'all';
|
|
290
|
+
var sortOrder = 'desc'; // 'desc' = newest first, 'asc' = chronological
|
|
291
|
+
var editingId = null;
|
|
292
|
+
|
|
293
|
+
var rail = document.getElementById('rail');
|
|
294
|
+
var filterRow = document.getElementById('filterRow');
|
|
295
|
+
var sortToggle = document.getElementById('sortToggle');
|
|
296
|
+
var drawerOverlay = document.getElementById('drawerOverlay');
|
|
297
|
+
var drawerTitle = document.getElementById('drawerTitle');
|
|
298
|
+
var dateIn = document.getElementById('evDate');
|
|
299
|
+
var titleIn = document.getElementById('evTitle');
|
|
300
|
+
var descIn = document.getElementById('evDesc');
|
|
301
|
+
var catIn = document.getElementById('evCat');
|
|
302
|
+
|
|
303
|
+
function escapeHtml(s) {
|
|
304
|
+
return String(s).replace(/[&<>"']/g, function (c) {
|
|
305
|
+
return ({ '&':'&','<':'<','>':'>','"':'"',"'":''' })[c];
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function fmtDate(iso) {
|
|
310
|
+
if (!iso) return '';
|
|
311
|
+
try {
|
|
312
|
+
var d = new Date(iso + 'T00:00:00');
|
|
313
|
+
if (isNaN(d.getTime())) return iso;
|
|
314
|
+
return d.toLocaleDateString([], { year: 'numeric', month: 'short', day: 'numeric' });
|
|
315
|
+
} catch (e) { return iso; }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function visibleEvents() {
|
|
319
|
+
var rows = events.filter(function (e) {
|
|
320
|
+
return activeCat === 'all' || e.category === activeCat;
|
|
321
|
+
});
|
|
322
|
+
rows.sort(function (a, b) {
|
|
323
|
+
var da = a.date || '';
|
|
324
|
+
var db = b.date || '';
|
|
325
|
+
return sortOrder === 'asc' ? da.localeCompare(db) : db.localeCompare(da);
|
|
326
|
+
});
|
|
327
|
+
return rows;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function render() {
|
|
331
|
+
var rows = visibleEvents();
|
|
332
|
+
if (!rows.length) {
|
|
333
|
+
rail.innerHTML = '<div class="tl-empty">' +
|
|
334
|
+
(events.length ? 'No events match this filter.' : 'No events yet — click Add Event to get started.') +
|
|
335
|
+
'</div>';
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
rail.innerHTML = rows.map(function (e) {
|
|
339
|
+
return '<div class="tl-event" data-id="' + e.id + '">' +
|
|
340
|
+
'<div class="tl-marker"></div>' +
|
|
341
|
+
'<div class="tl-card">' +
|
|
342
|
+
'<div class="tl-date">' + escapeHtml(fmtDate(e.date)) + '</div>' +
|
|
343
|
+
'<div class="tl-title">' + escapeHtml(e.title || 'Untitled') + '</div>' +
|
|
344
|
+
(e.description ? '<div class="tl-desc">' + escapeHtml(e.description) + '</div>' : '') +
|
|
345
|
+
(e.category ? '<span class="tl-cat">' + escapeHtml(e.category) + '</span>' : '') +
|
|
346
|
+
'</div>' +
|
|
347
|
+
'</div>';
|
|
348
|
+
}).join('');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function openDrawer(id) {
|
|
352
|
+
editingId = id;
|
|
353
|
+
if (id) {
|
|
354
|
+
var ev = events.find(function (x) { return x.id === id; });
|
|
355
|
+
if (!ev) return;
|
|
356
|
+
drawerTitle.textContent = 'Edit Event';
|
|
357
|
+
dateIn.value = ev.date || '';
|
|
358
|
+
titleIn.value = ev.title || '';
|
|
359
|
+
descIn.value = ev.description || '';
|
|
360
|
+
catIn.value = ev.category || 'milestone';
|
|
361
|
+
} else {
|
|
362
|
+
drawerTitle.textContent = 'New Event';
|
|
363
|
+
dateIn.value = new Date().toISOString().slice(0, 10);
|
|
364
|
+
titleIn.value = '';
|
|
365
|
+
descIn.value = '';
|
|
366
|
+
catIn.value = 'milestone';
|
|
367
|
+
}
|
|
368
|
+
document.getElementById('deleteBtn').style.visibility = id ? 'visible' : 'hidden';
|
|
369
|
+
drawerOverlay.classList.add('open');
|
|
370
|
+
setTimeout(function () { titleIn.focus(); }, 0);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function closeDrawer() {
|
|
374
|
+
drawerOverlay.classList.remove('open');
|
|
375
|
+
editingId = null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Card click -> open drawer
|
|
379
|
+
rail.addEventListener('click', function (e) {
|
|
380
|
+
var card = e.target.closest('.tl-event');
|
|
381
|
+
if (!card) return;
|
|
382
|
+
openDrawer(card.getAttribute('data-id'));
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Filter chips
|
|
386
|
+
filterRow.addEventListener('click', function (e) {
|
|
387
|
+
var chip = e.target.closest('.filter-chip');
|
|
388
|
+
if (!chip) return;
|
|
389
|
+
filterRow.querySelectorAll('.filter-chip').forEach(function (c) {
|
|
390
|
+
c.classList.toggle('active', c === chip);
|
|
391
|
+
});
|
|
392
|
+
activeCat = chip.getAttribute('data-cat');
|
|
393
|
+
render();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Sort toggle
|
|
397
|
+
sortToggle.addEventListener('click', function () {
|
|
398
|
+
sortOrder = sortOrder === 'desc' ? 'asc' : 'desc';
|
|
399
|
+
sortToggle.textContent = sortOrder === 'desc' ? 'Newest first' : 'Chronological';
|
|
400
|
+
render();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
document.getElementById('addEventBtn').addEventListener('click', function () { openDrawer(null); });
|
|
404
|
+
document.getElementById('drawerClose').addEventListener('click', closeDrawer);
|
|
405
|
+
document.getElementById('cancelBtn').addEventListener('click', closeDrawer);
|
|
406
|
+
drawerOverlay.addEventListener('click', function (e) {
|
|
407
|
+
if (e.target === drawerOverlay) closeDrawer();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
document.getElementById('saveBtn').addEventListener('click', function () {
|
|
411
|
+
var data = {
|
|
412
|
+
date: dateIn.value,
|
|
413
|
+
title: titleIn.value.trim() || 'Untitled',
|
|
414
|
+
description: descIn.value.trim(),
|
|
415
|
+
category: catIn.value
|
|
416
|
+
};
|
|
417
|
+
if (editingId) {
|
|
418
|
+
var ev = events.find(function (x) { return x.id === editingId; });
|
|
419
|
+
if (ev) Object.assign(ev, data);
|
|
420
|
+
} else {
|
|
421
|
+
data.id = 'e_' + Math.random().toString(36).slice(2, 9);
|
|
422
|
+
events.push(data);
|
|
423
|
+
}
|
|
424
|
+
// synthos.shared.data.save('timeline_events', data);
|
|
425
|
+
closeDrawer();
|
|
426
|
+
render();
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
document.getElementById('deleteBtn').addEventListener('click', function () {
|
|
430
|
+
if (!editingId) return;
|
|
431
|
+
if (!confirm('Delete this event?')) return;
|
|
432
|
+
events = events.filter(function (x) { return x.id !== editingId; });
|
|
433
|
+
// synthos.shared.data.remove('timeline_events', editingId);
|
|
434
|
+
closeDrawer();
|
|
435
|
+
render();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Load persisted events (uncomment to enable):
|
|
439
|
+
// synthos.shared.data.list('timeline_events').then(function (rows) {
|
|
440
|
+
// if (rows && rows.length) { events = rows; render(); }
|
|
441
|
+
// });
|
|
442
|
+
|
|
443
|
+
render();
|
|
444
|
+
})();
|
|
445
|
+
</script>
|
|
446
|
+
</body></html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Timeline",
|
|
3
|
+
"description": "Chronicle dated events on a vertical rail for project history, changelogs, biographies, or incident logs.",
|
|
4
|
+
"categories": [
|
|
5
|
+
"_Starters"
|
|
6
|
+
],
|
|
7
|
+
"pinned": false,
|
|
8
|
+
"showInAll": false,
|
|
9
|
+
"pageVersion": 3,
|
|
10
|
+
"mode": "unlocked",
|
|
11
|
+
"greeting": "Welcome to the Timeline starter — a chronological vertical rail with date-anchored event cards. Tell me what you'd like to chronicle (e.g. project history, changelog, biography, road-trip log, war-room incident timeline) and I'll wire the events, categories, and edit drawer.",
|
|
12
|
+
"firstRunGreeting": ""
|
|
13
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"role":"assistant","content":"Welcome to the Tutorial starter — a sequenced explainer scaffold. Tell me what you'd like to teach (e.g. an onboarding tour, a recipe, a getting-started guide, a repair walkthrough) and I'll wire the step rail, content blocks, and progress tracking."}]
|
|
Binary file
|