synthos 0.10.0 → 0.11.0

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.
Files changed (312) hide show
  1. package/README.md +5 -5
  2. package/default-pages/elevenlabs_effects_studio/chat-history.json +1 -0
  3. package/default-pages/elevenlabs_effects_studio/page.html +1345 -1363
  4. package/default-pages/elevenlabs_effects_studio/page.json +13 -11
  5. package/default-pages/elevenlabs_voice_studio/chat-history.json +1 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +782 -801
  7. package/default-pages/elevenlabs_voice_studio/page.json +13 -11
  8. package/default-pages/json_tools/chat-history.json +1 -0
  9. package/default-pages/json_tools/page.html +70 -90
  10. package/default-pages/json_tools/page.json +12 -10
  11. package/default-pages/my_notes/chat-history.json +1 -0
  12. package/default-pages/my_notes/page.html +115 -131
  13. package/default-pages/my_notes/page.json +14 -12
  14. package/default-pages/neon_asteroids/chat-history.json +1 -0
  15. package/default-pages/neon_asteroids/page.html +1777 -1803
  16. package/default-pages/neon_asteroids/page.json +14 -12
  17. package/default-pages/oregon_trail/chat-history.json +1 -0
  18. package/default-pages/oregon_trail/page.html +290 -307
  19. package/default-pages/oregon_trail/page.json +14 -12
  20. package/default-pages/solar_explorer/chat-history.json +1 -0
  21. package/default-pages/solar_explorer/page.html +1929 -1951
  22. package/default-pages/solar_explorer/page.json +14 -12
  23. package/default-pages/solar_tutorial/chat-history.json +1 -0
  24. package/default-pages/solar_tutorial/page.html +464 -478
  25. package/default-pages/solar_tutorial/page.json +12 -10
  26. package/default-pages/us_map/chat-history.json +1 -0
  27. package/default-pages/us_map/page.html +170 -193
  28. package/default-pages/us_map/page.json +14 -12
  29. package/default-pages/us_map/page.light.png +0 -0
  30. package/default-pages/us_map_1850/chat-history.json +1 -0
  31. package/default-pages/us_map_1850/page.html +302 -326
  32. package/default-pages/us_map_1850/page.json +14 -12
  33. package/default-pages/western_cities_1850/chat-history.json +1 -0
  34. package/default-pages/western_cities_1850/page.html +503 -527
  35. package/default-pages/western_cities_1850/page.json +14 -12
  36. package/default-themes/aurora-dawn.v3.css +15 -14
  37. package/default-themes/aurora-dusk.v3.css +26 -26
  38. package/default-themes/cosmos-dawn.v3.css +15 -14
  39. package/default-themes/cosmos-dusk.v3.css +26 -26
  40. package/default-themes/elemental-dawn.v3.css +200 -0
  41. package/default-themes/nebula-dawn.v3.css +15 -14
  42. package/default-themes/nebula-dusk.v3.css +24 -24
  43. package/default-themes/solar-flare-dawn.v3.css +15 -14
  44. package/default-themes/solar-flare-dusk.v3.css +26 -26
  45. package/dist/builders/anthropic.d.ts +26 -2
  46. package/dist/builders/anthropic.d.ts.map +1 -1
  47. package/dist/builders/anthropic.js +132 -31
  48. package/dist/builders/anthropic.js.map +1 -1
  49. package/dist/builders/claudecode.d.ts +13 -0
  50. package/dist/builders/claudecode.d.ts.map +1 -0
  51. package/dist/builders/claudecode.js +253 -0
  52. package/dist/builders/claudecode.js.map +1 -0
  53. package/dist/builders/index.d.ts +2 -1
  54. package/dist/builders/index.d.ts.map +1 -1
  55. package/dist/builders/index.js +8 -1
  56. package/dist/builders/index.js.map +1 -1
  57. package/dist/builders/openai.js +2 -1
  58. package/dist/builders/openai.js.map +1 -1
  59. package/dist/builders/types.d.ts +31 -7
  60. package/dist/builders/types.d.ts.map +1 -1
  61. package/dist/builders/types.js +60 -28
  62. package/dist/builders/types.js.map +1 -1
  63. package/dist/connectors/types.d.ts +8 -0
  64. package/dist/connectors/types.d.ts.map +1 -1
  65. package/dist/init.d.ts.map +1 -1
  66. package/dist/init.js +13 -6
  67. package/dist/init.js.map +1 -1
  68. package/dist/migrations.d.ts.map +1 -1
  69. package/dist/migrations.js +161 -14
  70. package/dist/migrations.js.map +1 -1
  71. package/dist/models/anthropic.d.ts +1 -0
  72. package/dist/models/anthropic.d.ts.map +1 -1
  73. package/dist/models/anthropic.js +129 -29
  74. package/dist/models/anthropic.js.map +1 -1
  75. package/dist/models/chainOfThought.d.ts.map +1 -1
  76. package/dist/models/chainOfThought.js +32 -19
  77. package/dist/models/chainOfThought.js.map +1 -1
  78. package/dist/models/index.d.ts +2 -2
  79. package/dist/models/index.d.ts.map +1 -1
  80. package/dist/models/index.js +2 -1
  81. package/dist/models/index.js.map +1 -1
  82. package/dist/models/providers.d.ts +1 -0
  83. package/dist/models/providers.d.ts.map +1 -1
  84. package/dist/models/providers.js +12 -4
  85. package/dist/models/providers.js.map +1 -1
  86. package/dist/models/types.d.ts +15 -1
  87. package/dist/models/types.d.ts.map +1 -1
  88. package/dist/models/types.js.map +1 -1
  89. package/dist/pages.d.ts +57 -8
  90. package/dist/pages.d.ts.map +1 -1
  91. package/dist/pages.js +258 -45
  92. package/dist/pages.js.map +1 -1
  93. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  94. package/dist/service/createCompletePrompt.js +5 -0
  95. package/dist/service/createCompletePrompt.js.map +1 -1
  96. package/dist/service/mediaCache.d.ts +36 -0
  97. package/dist/service/mediaCache.d.ts.map +1 -0
  98. package/dist/service/mediaCache.js +182 -0
  99. package/dist/service/mediaCache.js.map +1 -0
  100. package/dist/service/pageValidator.d.ts +25 -0
  101. package/dist/service/pageValidator.d.ts.map +1 -0
  102. package/dist/service/pageValidator.js +315 -0
  103. package/dist/service/pageValidator.js.map +1 -0
  104. package/dist/service/server.d.ts.map +1 -1
  105. package/dist/service/server.js +4 -0
  106. package/dist/service/server.js.map +1 -1
  107. package/dist/service/sharedTableSchema.d.ts +73 -0
  108. package/dist/service/sharedTableSchema.d.ts.map +1 -0
  109. package/dist/service/sharedTableSchema.js +206 -0
  110. package/dist/service/sharedTableSchema.js.map +1 -0
  111. package/dist/service/transformPage.d.ts +49 -11
  112. package/dist/service/transformPage.d.ts.map +1 -1
  113. package/dist/service/transformPage.js +354 -241
  114. package/dist/service/transformPage.js.map +1 -1
  115. package/dist/service/useApiRoutes.d.ts.map +1 -1
  116. package/dist/service/useApiRoutes.js +288 -34
  117. package/dist/service/useApiRoutes.js.map +1 -1
  118. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  119. package/dist/service/useConnectorRoutes.js +170 -32
  120. package/dist/service/useConnectorRoutes.js.map +1 -1
  121. package/dist/service/useDataRoutes.d.ts.map +1 -1
  122. package/dist/service/useDataRoutes.js +59 -2
  123. package/dist/service/useDataRoutes.js.map +1 -1
  124. package/dist/service/useExtractRoutes.d.ts +4 -0
  125. package/dist/service/useExtractRoutes.d.ts.map +1 -0
  126. package/dist/service/useExtractRoutes.js +304 -0
  127. package/dist/service/useExtractRoutes.js.map +1 -0
  128. package/dist/service/usePageRoutes.d.ts +17 -0
  129. package/dist/service/usePageRoutes.d.ts.map +1 -1
  130. package/dist/service/usePageRoutes.js +1385 -483
  131. package/dist/service/usePageRoutes.js.map +1 -1
  132. package/dist/service/useSharedDataRoutes.d.ts.map +1 -1
  133. package/dist/service/useSharedDataRoutes.js +54 -2
  134. package/dist/service/useSharedDataRoutes.js.map +1 -1
  135. package/dist/settings.d.ts +27 -0
  136. package/dist/settings.d.ts.map +1 -1
  137. package/dist/settings.js +40 -1
  138. package/dist/settings.js.map +1 -1
  139. package/dist/themes.d.ts +0 -5
  140. package/dist/themes.d.ts.map +1 -1
  141. package/dist/themes.js +3 -95
  142. package/dist/themes.js.map +1 -1
  143. package/migration-rules/v2-to-v3.md +277 -119
  144. package/package.json +5 -1
  145. package/{default-pages/application → required-pages/_shell}/page.html +56 -42
  146. package/required-pages/_shell/page.json +14 -0
  147. package/required-pages/_starters/page.html +534 -0
  148. package/required-pages/_starters/page.json +12 -0
  149. package/required-pages/builder/page.html +353 -43
  150. package/required-pages/builder/page.json +12 -10
  151. package/required-pages/pages/page.html +697 -924
  152. package/required-pages/pages/page.json +12 -10
  153. package/required-pages/settings/page.html +1879 -1753
  154. package/required-pages/settings/page.json +12 -10
  155. package/required-pages/synthos_apis/page.html +834 -845
  156. package/required-pages/synthos_apis/page.json +12 -10
  157. package/required-pages/synthos_scripts/page.html +74 -88
  158. package/required-pages/synthos_scripts/page.json +12 -10
  159. package/scripts/append-instructions.py +90 -0
  160. package/scripts/audit-instructions.py +76 -0
  161. package/scripts/cleanup-shell-markup.mjs +112 -0
  162. package/service-connectors/buffer/connector.json +46 -0
  163. package/service-connectors/canva/connector.json +67 -0
  164. package/service-connectors/elevenlabs/connector.json +1 -1
  165. package/src/builders/anthropic.ts +155 -30
  166. package/src/builders/claudecode.ts +310 -0
  167. package/src/builders/index.ts +7 -1
  168. package/src/builders/openai.ts +2 -1
  169. package/src/builders/types.ts +93 -32
  170. package/src/connectors/types.ts +8 -0
  171. package/src/init.ts +13 -7
  172. package/src/migrations.ts +187 -16
  173. package/src/models/anthropic.ts +140 -30
  174. package/src/models/chainOfThought.ts +33 -18
  175. package/src/models/index.ts +2 -2
  176. package/src/models/providers.ts +12 -3
  177. package/src/models/types.ts +21 -1
  178. package/src/pages.ts +271 -35
  179. package/src/service/createCompletePrompt.ts +6 -0
  180. package/src/service/mediaCache.ts +206 -0
  181. package/src/service/pageValidator.ts +337 -0
  182. package/src/service/server.ts +4 -0
  183. package/src/service/sharedTableSchema.ts +236 -0
  184. package/src/service/transformPage.ts +370 -260
  185. package/src/service/useApiRoutes.ts +282 -32
  186. package/src/service/useConnectorRoutes.ts +189 -34
  187. package/src/service/useDataRoutes.ts +198 -116
  188. package/src/service/useExtractRoutes.ts +331 -0
  189. package/src/service/usePageRoutes.ts +1411 -394
  190. package/src/service/useSharedDataRoutes.ts +184 -109
  191. package/src/settings.ts +65 -0
  192. package/src/themes.ts +78 -180
  193. package/starters/blank_starter/chat-history.json +1 -0
  194. package/starters/blank_starter/page.dark.png +0 -0
  195. package/starters/blank_starter/page.html +47 -0
  196. package/starters/blank_starter/page.json +13 -0
  197. package/starters/blank_starter/page.light.png +0 -0
  198. package/starters/calculator_starter/chat-history.json +1 -0
  199. package/starters/calculator_starter/page.dark.png +0 -0
  200. package/starters/calculator_starter/page.html +232 -0
  201. package/starters/calculator_starter/page.json +13 -0
  202. package/starters/calculator_starter/page.light.png +0 -0
  203. package/starters/calendar_starter/chat-history.json +1 -0
  204. package/starters/calendar_starter/page.dark.png +0 -0
  205. package/starters/calendar_starter/page.html +495 -0
  206. package/starters/calendar_starter/page.json +13 -0
  207. package/starters/calendar_starter/page.light.png +0 -0
  208. package/starters/chat_starter/chat-history.json +1 -0
  209. package/starters/chat_starter/page.dark.png +0 -0
  210. package/starters/chat_starter/page.html +351 -0
  211. package/starters/chat_starter/page.json +13 -0
  212. package/starters/chat_starter/page.light.png +0 -0
  213. package/starters/checklist_starter/chat-history.json +1 -0
  214. package/starters/checklist_starter/page.dark.png +0 -0
  215. package/starters/checklist_starter/page.html +437 -0
  216. package/starters/checklist_starter/page.json +13 -0
  217. package/starters/checklist_starter/page.light.png +0 -0
  218. package/starters/dashboard_starter/chat-history.json +1 -0
  219. package/starters/dashboard_starter/page.dark.png +0 -0
  220. package/starters/dashboard_starter/page.html +195 -0
  221. package/starters/dashboard_starter/page.json +13 -0
  222. package/starters/dashboard_starter/page.light.png +0 -0
  223. package/starters/form_starter/chat-history.json +1 -0
  224. package/starters/form_starter/page.dark.png +0 -0
  225. package/starters/form_starter/page.html +313 -0
  226. package/starters/form_starter/page.json +13 -0
  227. package/starters/form_starter/page.light.png +0 -0
  228. package/starters/gallery_starter/chat-history.json +1 -0
  229. package/starters/gallery_starter/page.dark.png +0 -0
  230. package/starters/gallery_starter/page.html +418 -0
  231. package/starters/gallery_starter/page.json +13 -0
  232. package/starters/gallery_starter/page.light.png +0 -0
  233. package/starters/generator_starter/chat-history.json +1 -0
  234. package/starters/generator_starter/page.dark.png +0 -0
  235. package/starters/generator_starter/page.html +261 -0
  236. package/starters/generator_starter/page.json +13 -0
  237. package/starters/generator_starter/page.light.png +0 -0
  238. package/starters/index.html +538 -0
  239. package/starters/kanban_starter/chat-history.json +1 -0
  240. package/starters/kanban_starter/page.dark.png +0 -0
  241. package/starters/kanban_starter/page.html +432 -0
  242. package/starters/kanban_starter/page.json +13 -0
  243. package/starters/kanban_starter/page.light.png +0 -0
  244. package/starters/presentation_builder/chat-history.json +1 -0
  245. package/starters/presentation_builder/page.dark.png +0 -0
  246. package/starters/presentation_builder/page.html +970 -0
  247. package/starters/presentation_builder/page.json +15 -0
  248. package/starters/presentation_builder/page.light.png +0 -0
  249. package/starters/presentation_builder/presentation_voice/voice_config.json +9 -0
  250. package/starters/pulse_starter/chat-history.json +1 -0
  251. package/starters/pulse_starter/page.dark.png +0 -0
  252. package/starters/pulse_starter/page.html +698 -0
  253. package/starters/pulse_starter/page.json +13 -0
  254. package/starters/pulse_starter/page.light.png +0 -0
  255. package/starters/quiz_starter/chat-history.json +1 -0
  256. package/starters/quiz_starter/page.dark.png +0 -0
  257. package/starters/quiz_starter/page.html +292 -0
  258. package/starters/quiz_starter/page.json +13 -0
  259. package/starters/quiz_starter/page.light.png +0 -0
  260. package/starters/reference_starter/chat-history.json +1 -0
  261. package/starters/reference_starter/page.dark.png +0 -0
  262. package/starters/reference_starter/page.html +250 -0
  263. package/starters/reference_starter/page.json +13 -0
  264. package/starters/reference_starter/page.light.png +0 -0
  265. package/starters/retro_game_starter/chat-history.json +1 -0
  266. package/starters/retro_game_starter/page.dark.png +0 -0
  267. package/{default-pages → starters}/retro_game_starter/page.html +1281 -1308
  268. package/starters/retro_game_starter/page.json +15 -0
  269. package/starters/retro_game_starter/page.light.png +0 -0
  270. package/starters/roster_starter/chat-history.json +1 -0
  271. package/starters/roster_starter/page.dark.png +0 -0
  272. package/starters/roster_starter/page.html +600 -0
  273. package/starters/roster_starter/page.json +13 -0
  274. package/starters/roster_starter/page.light.png +0 -0
  275. package/starters/server.js +182 -0
  276. package/starters/start.cmd +1 -0
  277. package/starters/timeline_starter/chat-history.json +1 -0
  278. package/starters/timeline_starter/page.dark.png +0 -0
  279. package/starters/timeline_starter/page.html +446 -0
  280. package/starters/timeline_starter/page.json +13 -0
  281. package/starters/timeline_starter/page.light.png +0 -0
  282. package/starters/tutorial_starter/chat-history.json +1 -0
  283. package/starters/tutorial_starter/page.dark.png +0 -0
  284. package/starters/tutorial_starter/page.html +283 -0
  285. package/starters/tutorial_starter/page.json +13 -0
  286. package/starters/tutorial_starter/page.light.png +0 -0
  287. package/static-files/agent.v3.js +122 -0
  288. package/static-files/connector.v3.js +48 -0
  289. package/static-files/extract.v3.js +188 -0
  290. package/static-files/helpers.v3.js +50 -6
  291. package/static-files/page-bridge.js +114 -0
  292. package/static-files/page.v3.js +1292 -1290
  293. package/static-files/script.v3.js +32 -0
  294. package/static-files/server.v3.js +89 -0
  295. package/static-files/shell-bridge.v3.js +174 -0
  296. package/static-files/shell-modals.v3.js +521 -0
  297. package/static-files/{shell.css → shell.v3.css} +271 -22
  298. package/static-files/shell.v3.js +1865 -0
  299. package/static-files/storage.v3.js +176 -0
  300. package/tests/anthropic.spec.ts +42 -7
  301. package/tests/builders.spec.ts +72 -4
  302. package/tests/pageValidator.spec.ts +548 -0
  303. package/tests/profiles.spec.ts +122 -0
  304. package/tests/providers.spec.ts +1 -1
  305. package/tests/sharedTableSchema.spec.ts +242 -0
  306. package/tests/transformPage.spec.ts +62 -81
  307. package/default-pages/application/page.json +0 -10
  308. package/default-pages/retro_game_starter/page.json +0 -12
  309. package/default-pages/sidebar_page/page.html +0 -51
  310. package/default-pages/sidebar_page/page.json +0 -10
  311. package/default-pages/two-panel_page/page.html +0 -68
  312. package/default-pages/two-panel_page/page.json +0 -10
@@ -0,0 +1,15 @@
1
+ {
2
+ "title": "Retro Game",
3
+ "description": "Build a classic-style arcade game with game pad support and a simple game loop.",
4
+ "categories": [
5
+ "_Starters"
6
+ ],
7
+ "pinned": false,
8
+ "showInAll": true,
9
+ "createdDate": "2026-03-02T12:00:00.000Z",
10
+ "lastModified": "2026-03-02T12:00:00.000Z",
11
+ "pageVersion": 3,
12
+ "mode": "unlocked",
13
+ "greeting": "Welcome to the Retro Game Starter! This is a starting point for building your game.",
14
+ "firstRunGreeting": ""
15
+ }
@@ -0,0 +1 @@
1
+ [{"role":"assistant","content":"Welcome to the Roster starter — Archetype D from BLUEPRINT.md. Use this scaffold for table editors that own a shared data table: row list with search/filter chips, click-row-to-edit drawer, and optional bulk-import via __synthOSExtract. Tell me what to track (employees, equipment, snapshots, supplies) and I'll wire the schema, table columns, and drawer fields."}]
@@ -0,0 +1,600 @@
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 - Roster 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
+ .flm-dialog-overlay { z-index: 1100; }
9
+
10
+ .roster-app {
11
+ display: flex;
12
+ flex-direction: column;
13
+ height: 100%;
14
+ width: 100%;
15
+ overflow: hidden;
16
+ background: var(--bodyBackground, #ffffff);
17
+ }
18
+ .roster-header {
19
+ display: flex;
20
+ align-items: center;
21
+ gap: 16px;
22
+ padding: 16px 24px;
23
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
24
+ background: var(--defaultStateBackground, #faf9f8);
25
+ flex-shrink: 0;
26
+ }
27
+ .roster-header .header-titles {
28
+ display: flex;
29
+ flex-direction: column;
30
+ flex: 1;
31
+ min-width: 0;
32
+ }
33
+ .roster-summary {
34
+ display: flex;
35
+ gap: 12px;
36
+ padding: 12px 24px;
37
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
38
+ flex-shrink: 0;
39
+ align-items: center;
40
+ flex-wrap: wrap;
41
+ }
42
+ .summary-stat {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 2px;
46
+ }
47
+ .summary-stat .stat-value {
48
+ font-size: 18px;
49
+ font-weight: 600;
50
+ color: var(--bodyText, #333333);
51
+ }
52
+ .summary-stat .stat-label {
53
+ font-size: 12px;
54
+ color: var(--bodySubtext, #605e5c);
55
+ }
56
+ .roster-toolbar {
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 12px;
60
+ padding: 12px 24px;
61
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
62
+ flex-wrap: wrap;
63
+ flex-shrink: 0;
64
+ }
65
+ .filter-row {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 8px;
69
+ flex-wrap: wrap;
70
+ }
71
+ .filter-row .filter-label {
72
+ font-size: 12px;
73
+ color: var(--bodySubtext, #605e5c);
74
+ }
75
+ .filter-chip {
76
+ padding: 6px 12px;
77
+ border-radius: 16px;
78
+ border: 1px solid var(--inputBorder, #d2d0ce);
79
+ background: var(--bodyBackground, #ffffff);
80
+ color: var(--bodyText, #333333);
81
+ font-size: 13px;
82
+ cursor: pointer;
83
+ }
84
+ .filter-chip:hover { background: var(--bodyBackgroundHovered, #f3f2f1); }
85
+ .filter-chip.active {
86
+ background: var(--themePrimary, #0078d4);
87
+ border-color: var(--themePrimary, #0078d4);
88
+ color: #ffffff;
89
+ }
90
+ .toolbar-search {
91
+ flex: 1;
92
+ min-width: 180px;
93
+ }
94
+ .toolbar-search input {
95
+ width: 100%;
96
+ padding: 6px 10px;
97
+ border: 1px solid var(--inputBorder, #d2d0ce);
98
+ border-radius: 4px;
99
+ background: var(--bodyBackground, #ffffff);
100
+ color: var(--bodyText, #333333);
101
+ box-sizing: border-box;
102
+ }
103
+ .roster-body {
104
+ flex: 1;
105
+ overflow: auto;
106
+ padding: 0 24px 24px;
107
+ }
108
+ .row-table {
109
+ width: 100%;
110
+ border-collapse: collapse;
111
+ margin-top: 12px;
112
+ }
113
+ .row-table th,
114
+ .row-table td {
115
+ text-align: left;
116
+ padding: 10px 12px;
117
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
118
+ font-size: 13px;
119
+ color: var(--bodyText, #333333);
120
+ }
121
+ .row-table th {
122
+ font-weight: 600;
123
+ color: var(--bodySubtext, #605e5c);
124
+ background: var(--defaultStateBackground, #faf9f8);
125
+ position: sticky;
126
+ top: 0;
127
+ }
128
+ .row-table tbody tr { cursor: pointer; }
129
+ .row-table tbody tr:hover { background: var(--bodyBackgroundHovered, #f3f2f1); }
130
+ .roster-empty {
131
+ text-align: center;
132
+ color: var(--bodySubtext, #605e5c);
133
+ padding: 32px 0;
134
+ font-size: 13px;
135
+ }
136
+
137
+ /* Import panel */
138
+ .import-card {
139
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
140
+ background: var(--bodyBackground, #ffffff);
141
+ flex-shrink: 0;
142
+ }
143
+ .import-head {
144
+ display: flex;
145
+ align-items: center;
146
+ padding: 10px 24px;
147
+ cursor: pointer;
148
+ user-select: none;
149
+ font-size: 13px;
150
+ font-weight: 600;
151
+ color: var(--bodyText, #333333);
152
+ }
153
+ .import-head .caret {
154
+ margin-right: 8px;
155
+ transition: transform 0.15s;
156
+ }
157
+ .import-card.open .import-head .caret { transform: rotate(90deg); }
158
+ .import-body {
159
+ display: none;
160
+ padding: 0 24px 14px;
161
+ }
162
+ .import-card.open .import-body { display: block; }
163
+ .import-dropzone {
164
+ border: 2px dashed var(--inputBorder, #d2d0ce);
165
+ border-radius: 6px;
166
+ padding: 24px;
167
+ text-align: center;
168
+ cursor: pointer;
169
+ color: var(--bodySubtext, #605e5c);
170
+ font-size: 13px;
171
+ }
172
+ .import-dropzone:hover,
173
+ .import-dropzone.dragover {
174
+ border-color: var(--themePrimary, #0078d4);
175
+ background: var(--bodyBackgroundHovered, #f3f2f1);
176
+ }
177
+ .import-status {
178
+ font-size: 12px;
179
+ margin-top: 8px;
180
+ color: var(--bodySubtext, #605e5c);
181
+ }
182
+ .import-status.error { color: var(--errorText, #a4262c); }
183
+
184
+ /* Drawer overlay + panel — required local CSS for v3 iframe pages */
185
+ .drawer-overlay {
186
+ position: fixed;
187
+ top: 0; left: 0; right: 0; bottom: 0;
188
+ background: rgba(0, 0, 0, 0.4);
189
+ display: none;
190
+ z-index: 1500;
191
+ }
192
+ .drawer-overlay.show { display: block; }
193
+ .drawer-panel {
194
+ position: fixed;
195
+ top: 0;
196
+ right: 0;
197
+ bottom: 0;
198
+ width: 420px;
199
+ max-width: 100vw;
200
+ background: var(--bodyBackground, #ffffff);
201
+ border-left: 1px solid var(--bodyDivider, #edebe9);
202
+ box-shadow: -4px 0 16px rgba(0, 0, 0, 0.08);
203
+ display: flex;
204
+ flex-direction: column;
205
+ transform: translateX(100%);
206
+ transition: transform 0.2s ease-out;
207
+ z-index: 1600;
208
+ }
209
+ .drawer-panel.show { transform: translateX(0); }
210
+ .drawer-panel .drawer-head {
211
+ display: flex;
212
+ align-items: center;
213
+ padding: 14px 20px;
214
+ border-bottom: 1px solid var(--bodyDivider, #edebe9);
215
+ font-size: 16px;
216
+ font-weight: 600;
217
+ color: var(--bodyText, #333333);
218
+ }
219
+ .drawer-panel .drawer-head .close {
220
+ margin-left: auto;
221
+ background: transparent;
222
+ border: 0;
223
+ font-size: 20px;
224
+ cursor: pointer;
225
+ color: var(--bodySubtext, #605e5c);
226
+ }
227
+ .drawer-panel .drawer-form {
228
+ flex: 1;
229
+ overflow: auto;
230
+ padding: 16px 20px;
231
+ display: grid;
232
+ grid-template-columns: 1fr 1fr;
233
+ gap: 12px 16px;
234
+ }
235
+ .drawer-form .field-label {
236
+ display: block;
237
+ font-size: 12px;
238
+ color: var(--bodySubtext, #605e5c);
239
+ margin-bottom: 4px;
240
+ }
241
+ .drawer-form input[type="text"],
242
+ .drawer-form input[type="number"],
243
+ .drawer-form textarea {
244
+ width: 100%;
245
+ padding: 6px 10px;
246
+ border: 1px solid var(--inputBorder, #d2d0ce);
247
+ border-radius: 2px;
248
+ background: var(--bodyBackground, #ffffff);
249
+ color: var(--bodyText, #333333);
250
+ box-sizing: border-box;
251
+ font: inherit;
252
+ }
253
+ .drawer-form .span-2 { grid-column: 1 / -1; }
254
+ .drawer-panel .drawer-foot {
255
+ display: flex;
256
+ gap: 8px;
257
+ padding: 12px 20px;
258
+ border-top: 1px solid var(--bodyDivider, #edebe9);
259
+ justify-content: flex-end;
260
+ }
261
+ </style>
262
+ </head>
263
+ <body>
264
+ <div class="viewer-panel" id="viewerPanel" style="padding: 0;">
265
+ <div class="roster-app">
266
+ <div class="roster-header">
267
+ <div class="header-titles">
268
+ <span class="flm-text flm-text--xLarge flm-text--bold flm-text--block">[Page Title]</span>
269
+ <span class="flm-text flm-text--small flm-text--secondary flm-text--block">[Subtitle — what this roster is for]</span>
270
+ </div>
271
+ </div>
272
+
273
+ <div class="import-card" id="importCard">
274
+ <div class="import-head" id="importHead">
275
+ <span class="caret">▶</span>
276
+ <span>Bulk import — drop a file</span>
277
+ </div>
278
+ <div class="import-body">
279
+ <div class="import-dropzone" id="importDropZone">
280
+ Drop a file here, or click to choose<br><span style="font-size:11px;">CSV, TSV, or PDF</span>
281
+ </div>
282
+ <div class="import-status" id="importStatus"></div>
283
+ </div>
284
+ </div>
285
+
286
+ <div class="roster-summary">
287
+ <div class="summary-stat"><span class="stat-value" id="rowCount">0</span><span class="stat-label">Rows</span></div>
288
+ <div class="summary-stat"><span class="stat-value" id="lastEdited">—</span><span class="stat-label">Last edited</span></div>
289
+ <div style="margin-left:auto;">
290
+ <button class="flm-button flm-button--primary" id="addBtn" data-icon="Add">Add row</button>
291
+ </div>
292
+ </div>
293
+
294
+ <div class="roster-toolbar">
295
+ <div class="filter-row" id="filterRow">
296
+ <span class="filter-label">Filter:</span>
297
+ <button class="filter-chip active" data-filter="all">All</button>
298
+ </div>
299
+ <div class="toolbar-search">
300
+ <input id="searchInput" type="text" placeholder="Search...">
301
+ </div>
302
+ </div>
303
+
304
+ <div class="roster-body" id="bodyWrap">
305
+ <table class="row-table" id="rowTable">
306
+ <thead>
307
+ <tr>
308
+ <th>Name</th>
309
+ <th>Status</th>
310
+ <th>Notes</th>
311
+ </tr>
312
+ </thead>
313
+ <tbody id="rowTbody"></tbody>
314
+ </table>
315
+ <div class="roster-empty" id="emptyState">No rows yet — click "Add row" to create one.</div>
316
+ </div>
317
+ </div>
318
+
319
+ <!-- Drawer -->
320
+ <div class="drawer-overlay" id="drawerOverlay"></div>
321
+ <div class="drawer-panel" id="drawerPanel" role="dialog" aria-modal="true">
322
+ <div class="drawer-head">
323
+ <span id="drawerTitle">Add row</span>
324
+ <button class="close" id="drawerClose" aria-label="Close">×</button>
325
+ </div>
326
+ <div class="drawer-form">
327
+ <div class="span-2">
328
+ <label class="field-label" for="f-name">Name</label>
329
+ <input id="f-name" type="text">
330
+ </div>
331
+ <div>
332
+ <label class="field-label" for="f-status">Status</label>
333
+ <input id="f-status" type="text">
334
+ </div>
335
+ <div>
336
+ <label class="field-label" for="f-priority">Priority</label>
337
+ <input id="f-priority" type="number">
338
+ </div>
339
+ <div class="span-2">
340
+ <label class="field-label" for="f-notes">Notes</label>
341
+ <textarea id="f-notes" rows="4"></textarea>
342
+ </div>
343
+ </div>
344
+ <div class="drawer-foot">
345
+ <button class="flm-button" id="deleteBtn" style="margin-right:auto;color:var(--errorText,#a4262c);display:none;" data-icon="Delete">Delete</button>
346
+ <button class="flm-button" id="cancelBtn">Cancel</button>
347
+ <button class="flm-button flm-button--primary" id="saveBtn" data-icon="Save">Save</button>
348
+ </div>
349
+ </div>
350
+ </div>
351
+
352
+ <div id="instructions" style="display: none;" data-locked="true">Roster archetype (BLUEPRINT.md §4.5). Layout: header → import panel → summary bar (count + Add Row) → filter chips + search → table → drawer for edit. Persist rows via synthos.shared.data.save(TABLE, record) and load via synthos.shared.data.list(TABLE). The TABLE constant near the top of the script is the canonical owner of this data — only one page should own a given shared table. Bulk import uses window.__synthOSExtract({ schema, prompt, accept }) and yields parsed rows that the user confirms before bulk-saving. Required: viewer-panel must keep style="padding: 0;". Use theme tokens (avoid the natural pallet) unless the user requests an alternative color.</div>
353
+ <div id="thoughts" style="display: none;" data-locked="true"></div>
354
+
355
+ <script>
356
+ (function () {
357
+ // Replace with the actual shared-table name this page owns.
358
+ var TABLE = 'roster_rows';
359
+
360
+ var rows = [];
361
+ var editingId = null;
362
+ var filterValue = 'all';
363
+ var searchTerm = '';
364
+
365
+ // --- DOM refs ---
366
+ var tbody = document.getElementById('rowTbody');
367
+ var emptyState = document.getElementById('emptyState');
368
+ var rowCountEl = document.getElementById('rowCount');
369
+ var lastEditedEl = document.getElementById('lastEdited');
370
+ var drawerOverlay = document.getElementById('drawerOverlay');
371
+ var drawerPanel = document.getElementById('drawerPanel');
372
+ var drawerTitle = document.getElementById('drawerTitle');
373
+ var deleteBtn = document.getElementById('deleteBtn');
374
+
375
+ // --- wire UI ---
376
+ document.getElementById('addBtn').addEventListener('click', function () { openDrawer(null); });
377
+ document.getElementById('drawerClose').addEventListener('click', closeDrawer);
378
+ document.getElementById('cancelBtn').addEventListener('click', closeDrawer);
379
+ drawerOverlay.addEventListener('click', closeDrawer);
380
+ document.getElementById('saveBtn').addEventListener('click', saveEdit);
381
+ deleteBtn.addEventListener('click', deleteCurrent);
382
+
383
+ document.getElementById('filterRow').addEventListener('click', function (e) {
384
+ var chip = e.target.closest('.filter-chip');
385
+ if (!chip) return;
386
+ filterValue = chip.getAttribute('data-filter');
387
+ document.querySelectorAll('.filter-chip').forEach(function (c) {
388
+ c.classList.toggle('active', c === chip);
389
+ });
390
+ renderRows();
391
+ });
392
+
393
+ document.getElementById('searchInput').addEventListener('input', function (e) {
394
+ searchTerm = e.target.value.toLowerCase().trim();
395
+ renderRows();
396
+ });
397
+
398
+ tbody.addEventListener('click', function (e) {
399
+ var tr = e.target.closest('tr[data-row-id]');
400
+ if (tr) openDrawer(tr.getAttribute('data-row-id'));
401
+ });
402
+
403
+ // --- import wiring ---
404
+ var importCard = document.getElementById('importCard');
405
+ document.getElementById('importHead').addEventListener('click', function () {
406
+ importCard.classList.toggle('open');
407
+ });
408
+ wireImport();
409
+
410
+ // --- load + render ---
411
+ loadRows();
412
+
413
+ function loadRows() {
414
+ if (!(window.synthos && window.synthos.shared && window.synthos.shared.data)) {
415
+ rows = [];
416
+ renderAll();
417
+ return;
418
+ }
419
+ window.synthos.shared.data.list(TABLE).then(function (list) {
420
+ rows = list || [];
421
+ renderAll();
422
+ }, function () {
423
+ rows = [];
424
+ renderAll();
425
+ });
426
+ }
427
+
428
+ function renderAll() {
429
+ rowCountEl.textContent = String(rows.length);
430
+ renderRows();
431
+ }
432
+
433
+ function renderRows() {
434
+ var visible = rows.filter(function (r) {
435
+ if (filterValue !== 'all' && r.status !== filterValue) return false;
436
+ if (searchTerm) {
437
+ var hay = ((r.name || '') + ' ' + (r.notes || '')).toLowerCase();
438
+ if (hay.indexOf(searchTerm) === -1) return false;
439
+ }
440
+ return true;
441
+ });
442
+ if (!visible.length) {
443
+ tbody.innerHTML = '';
444
+ emptyState.style.display = 'block';
445
+ return;
446
+ }
447
+ emptyState.style.display = 'none';
448
+ tbody.innerHTML = visible.map(function (r) {
449
+ return ''
450
+ + '<tr data-row-id="' + r.id + '">'
451
+ + '<td>' + escapeHtml(r.name || '') + '</td>'
452
+ + '<td>' + escapeHtml(r.status || '') + '</td>'
453
+ + '<td>' + escapeHtml((r.notes || '').slice(0, 80)) + '</td>'
454
+ + '</tr>';
455
+ }).join('');
456
+ }
457
+
458
+ function openDrawer(id) {
459
+ editingId = id;
460
+ var r = id ? rows.find(function (x) { return x.id === id; }) : {};
461
+ drawerTitle.textContent = id ? 'Edit row' : 'Add row';
462
+ deleteBtn.style.display = id ? '' : 'none';
463
+ document.getElementById('f-name').value = r.name || '';
464
+ document.getElementById('f-status').value = r.status || '';
465
+ document.getElementById('f-priority').value = r.priority != null ? r.priority : '';
466
+ document.getElementById('f-notes').value = r.notes || '';
467
+ drawerOverlay.classList.add('show');
468
+ drawerPanel.classList.add('show');
469
+ }
470
+
471
+ function closeDrawer() {
472
+ editingId = null;
473
+ drawerOverlay.classList.remove('show');
474
+ drawerPanel.classList.remove('show');
475
+ }
476
+
477
+ function saveEdit() {
478
+ var rec = {
479
+ id: editingId || (Date.now() + '-' + Math.random().toString(36).slice(2, 8)),
480
+ name: document.getElementById('f-name').value.trim(),
481
+ status: document.getElementById('f-status').value.trim(),
482
+ priority: parseInt(document.getElementById('f-priority').value, 10) || 0,
483
+ notes: document.getElementById('f-notes').value.trim()
484
+ };
485
+ if (!(window.synthos && window.synthos.shared && window.synthos.shared.data)) {
486
+ upsertLocal(rec);
487
+ closeDrawer();
488
+ return;
489
+ }
490
+ window.synthos.shared.data.save(TABLE, rec).then(function (saved) {
491
+ upsertLocal(saved || rec);
492
+ closeDrawer();
493
+ }, function (err) {
494
+ alert('Save failed: ' + (err && err.message || 'unknown'));
495
+ });
496
+ }
497
+
498
+ function upsertLocal(rec) {
499
+ var idx = rows.findIndex(function (r) { return r.id === rec.id; });
500
+ if (idx >= 0) rows[idx] = rec; else rows.push(rec);
501
+ lastEditedEl.textContent = new Date().toLocaleString();
502
+ renderAll();
503
+ }
504
+
505
+ function deleteCurrent() {
506
+ if (!editingId) return;
507
+ if (!confirm('Delete this row?')) return;
508
+ if (!(window.synthos && window.synthos.shared && window.synthos.shared.data)) {
509
+ rows = rows.filter(function (r) { return r.id !== editingId; });
510
+ closeDrawer(); renderAll();
511
+ return;
512
+ }
513
+ window.synthos.shared.data.remove(TABLE, editingId).then(function () {
514
+ rows = rows.filter(function (r) { return r.id !== editingId; });
515
+ closeDrawer(); renderAll();
516
+ });
517
+ }
518
+
519
+ // --- import ---
520
+ function wireImport() {
521
+ var EXTRACT_OPTS = {
522
+ schema: {
523
+ type: 'object',
524
+ properties: {
525
+ rows: {
526
+ type: 'array',
527
+ items: {
528
+ type: 'object',
529
+ properties: {
530
+ name: { type: 'string' },
531
+ status: { type: 'string' },
532
+ priority: { type: 'number' },
533
+ notes: { type: 'string' }
534
+ }
535
+ }
536
+ }
537
+ },
538
+ required: ['rows']
539
+ },
540
+ prompt: 'Extract roster rows from this file. Each row should map to one record with name, status, priority (number), and notes. Return JSON matching the schema.',
541
+ accept: '.csv,.tsv,.txt,.pdf,image/*'
542
+ };
543
+ var dz = document.getElementById('importDropZone');
544
+ var status = document.getElementById('importStatus');
545
+
546
+ dz.addEventListener('click', function () {
547
+ if (!window.__synthOSExtract) {
548
+ setStatus('Upload helper unavailable on this page.', 'error');
549
+ return;
550
+ }
551
+ setStatus('Reading file...', '');
552
+ window.__synthOSExtract(EXTRACT_OPTS).then(applyImport, function (err) {
553
+ setStatus('Could not read file: ' + (err && err.message || 'unknown'), 'error');
554
+ });
555
+ });
556
+
557
+ if (window.__synthOSExtract && typeof window.__synthOSExtract.dropZone === 'function') {
558
+ window.__synthOSExtract.dropZone(dz, EXTRACT_OPTS, function (r) {
559
+ dz.classList.remove('dragover');
560
+ applyImport(r);
561
+ }, function (err) {
562
+ dz.classList.remove('dragover');
563
+ setStatus('Could not read file: ' + (err && err.message || 'unknown'), 'error');
564
+ });
565
+ }
566
+
567
+ function applyImport(result) {
568
+ var imported = result && result.data && Array.isArray(result.data.rows) ? result.data.rows : [];
569
+ if (!imported.length) {
570
+ setStatus('No rows extracted.', 'error');
571
+ return;
572
+ }
573
+ setStatus('Imported ' + imported.length + ' row(s).', '');
574
+ imported.forEach(function (r) {
575
+ r.id = Date.now() + '-' + Math.random().toString(36).slice(2, 8);
576
+ if (window.synthos && window.synthos.shared && window.synthos.shared.data) {
577
+ window.synthos.shared.data.save(TABLE, r).then(upsertLocal);
578
+ } else {
579
+ upsertLocal(r);
580
+ }
581
+ });
582
+ }
583
+
584
+ function setStatus(text, kind) {
585
+ status.textContent = text;
586
+ status.className = 'import-status' + (kind ? ' ' + kind : '');
587
+ }
588
+ }
589
+
590
+ function escapeHtml(s) {
591
+ return String(s)
592
+ .replace(/&/g, '&amp;')
593
+ .replace(/</g, '&lt;')
594
+ .replace(/>/g, '&gt;')
595
+ .replace(/"/g, '&quot;')
596
+ .replace(/'/g, '&#39;');
597
+ }
598
+ })();
599
+ </script>
600
+ </body></html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "title": "Roster",
3
+ "description": "Manage a shared data table with search, filters, and click-to-edit drawer for employees, equipment, or supplies.",
4
+ "categories": [
5
+ "_Starters"
6
+ ],
7
+ "pinned": false,
8
+ "showInAll": false,
9
+ "pageVersion": 3,
10
+ "mode": "unlocked",
11
+ "greeting": "Welcome to the Roster starter — Archetype D from BLUEPRINT.md. Use this scaffold for table editors that own a shared data table: row list with search/filter chips, click-row-to-edit drawer, and optional bulk-import via __synthOSExtract. Tell me what to track (employees, equipment, snapshots, supplies) and I'll wire the schema, table columns, and drawer fields.",
12
+ "firstRunGreeting": ""
13
+ }