sh3-core 0.6.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 (242) hide show
  1. package/README.md +9 -0
  2. package/dist/Shell.svelte +283 -0
  3. package/dist/Shell.svelte.d.ts +5 -0
  4. package/dist/api.d.ts +28 -0
  5. package/dist/api.js +50 -0
  6. package/dist/app/admin/ApiKeysView.svelte +169 -0
  7. package/dist/app/admin/ApiKeysView.svelte.d.ts +3 -0
  8. package/dist/app/admin/AuthSettingsView.svelte +105 -0
  9. package/dist/app/admin/AuthSettingsView.svelte.d.ts +3 -0
  10. package/dist/app/admin/SystemView.svelte +73 -0
  11. package/dist/app/admin/SystemView.svelte.d.ts +3 -0
  12. package/dist/app/admin/UsersView.svelte +188 -0
  13. package/dist/app/admin/UsersView.svelte.d.ts +3 -0
  14. package/dist/app/admin/adminApp.d.ts +7 -0
  15. package/dist/app/admin/adminApp.js +25 -0
  16. package/dist/app/admin/adminShard.svelte.d.ts +4 -0
  17. package/dist/app/admin/adminShard.svelte.js +62 -0
  18. package/dist/app/store/InstalledView.svelte +246 -0
  19. package/dist/app/store/InstalledView.svelte.d.ts +3 -0
  20. package/dist/app/store/StoreView.svelte +522 -0
  21. package/dist/app/store/StoreView.svelte.d.ts +3 -0
  22. package/dist/app/store/storeApp.d.ts +10 -0
  23. package/dist/app/store/storeApp.js +26 -0
  24. package/dist/app/store/storeShard.svelte.d.ts +38 -0
  25. package/dist/app/store/storeShard.svelte.js +218 -0
  26. package/dist/apps/lifecycle.d.ts +42 -0
  27. package/dist/apps/lifecycle.js +184 -0
  28. package/dist/apps/registry.svelte.d.ts +40 -0
  29. package/dist/apps/registry.svelte.js +59 -0
  30. package/dist/apps/terminal/manifest.d.ts +8 -0
  31. package/dist/apps/terminal/manifest.js +13 -0
  32. package/dist/apps/terminal/terminal-app.d.ts +7 -0
  33. package/dist/apps/terminal/terminal-app.js +14 -0
  34. package/dist/apps/types.d.ts +93 -0
  35. package/dist/apps/types.js +10 -0
  36. package/dist/artifact.d.ts +32 -0
  37. package/dist/artifact.js +1 -0
  38. package/dist/assets/SH3.png +0 -0
  39. package/dist/assets/icons.svg +1126 -0
  40. package/dist/assets.d.ts +13 -0
  41. package/dist/auth/GuestBanner.svelte +134 -0
  42. package/dist/auth/GuestBanner.svelte.d.ts +3 -0
  43. package/dist/auth/SignInWall.svelte +203 -0
  44. package/dist/auth/SignInWall.svelte.d.ts +7 -0
  45. package/dist/auth/auth.svelte.d.ts +69 -0
  46. package/dist/auth/auth.svelte.js +165 -0
  47. package/dist/auth/index.d.ts +2 -0
  48. package/dist/auth/index.js +1 -0
  49. package/dist/auth/types.d.ts +41 -0
  50. package/dist/auth/types.js +6 -0
  51. package/dist/build.d.ts +49 -0
  52. package/dist/build.js +236 -0
  53. package/dist/contract.d.ts +20 -0
  54. package/dist/contract.js +28 -0
  55. package/dist/createShell.d.ts +24 -0
  56. package/dist/createShell.js +131 -0
  57. package/dist/documents/backends.d.ts +17 -0
  58. package/dist/documents/backends.js +156 -0
  59. package/dist/documents/config.d.ts +7 -0
  60. package/dist/documents/config.js +27 -0
  61. package/dist/documents/handle.d.ts +6 -0
  62. package/dist/documents/handle.js +154 -0
  63. package/dist/documents/http-backend.d.ts +22 -0
  64. package/dist/documents/http-backend.js +78 -0
  65. package/dist/documents/index.d.ts +6 -0
  66. package/dist/documents/index.js +8 -0
  67. package/dist/documents/notifications.d.ts +9 -0
  68. package/dist/documents/notifications.js +39 -0
  69. package/dist/documents/types.d.ts +97 -0
  70. package/dist/documents/types.js +12 -0
  71. package/dist/env/client.d.ts +44 -0
  72. package/dist/env/client.js +106 -0
  73. package/dist/env/index.d.ts +2 -0
  74. package/dist/env/index.js +1 -0
  75. package/dist/env/types.d.ts +12 -0
  76. package/dist/env/types.js +8 -0
  77. package/dist/host-entry.d.ts +13 -0
  78. package/dist/host-entry.js +17 -0
  79. package/dist/host.d.ts +15 -0
  80. package/dist/host.js +86 -0
  81. package/dist/index.d.ts +4 -0
  82. package/dist/index.js +14 -0
  83. package/dist/layout/DragPreview.svelte +63 -0
  84. package/dist/layout/DragPreview.svelte.d.ts +3 -0
  85. package/dist/layout/LayoutRenderer.svelte +262 -0
  86. package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
  87. package/dist/layout/SlotContainer.svelte +140 -0
  88. package/dist/layout/SlotContainer.svelte.d.ts +8 -0
  89. package/dist/layout/SlotDropZone.svelte +122 -0
  90. package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
  91. package/dist/layout/drag.svelte.d.ts +45 -0
  92. package/dist/layout/drag.svelte.js +200 -0
  93. package/dist/layout/inspection.d.ts +72 -0
  94. package/dist/layout/inspection.js +209 -0
  95. package/dist/layout/ops.d.ts +100 -0
  96. package/dist/layout/ops.js +310 -0
  97. package/dist/layout/slotHostPool.svelte.d.ts +36 -0
  98. package/dist/layout/slotHostPool.svelte.js +229 -0
  99. package/dist/layout/store.svelte.d.ts +39 -0
  100. package/dist/layout/store.svelte.js +153 -0
  101. package/dist/layout/tree-walk.d.ts +15 -0
  102. package/dist/layout/tree-walk.js +33 -0
  103. package/dist/layout/types.d.ts +108 -0
  104. package/dist/layout/types.js +25 -0
  105. package/dist/migrations/shell-rename.d.ts +16 -0
  106. package/dist/migrations/shell-rename.js +48 -0
  107. package/dist/overlays/ModalFrame.svelte +87 -0
  108. package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
  109. package/dist/overlays/PopupFrame.svelte +85 -0
  110. package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
  111. package/dist/overlays/ToastItem.svelte +77 -0
  112. package/dist/overlays/ToastItem.svelte.d.ts +9 -0
  113. package/dist/overlays/focusTrap.d.ts +1 -0
  114. package/dist/overlays/focusTrap.js +64 -0
  115. package/dist/overlays/modal.d.ts +9 -0
  116. package/dist/overlays/modal.js +141 -0
  117. package/dist/overlays/popup.d.ts +9 -0
  118. package/dist/overlays/popup.js +108 -0
  119. package/dist/overlays/roots.d.ts +4 -0
  120. package/dist/overlays/roots.js +31 -0
  121. package/dist/overlays/toast.d.ts +6 -0
  122. package/dist/overlays/toast.js +93 -0
  123. package/dist/overlays/types.d.ts +31 -0
  124. package/dist/overlays/types.js +15 -0
  125. package/dist/platform/index.d.ts +10 -0
  126. package/dist/platform/index.js +33 -0
  127. package/dist/platform/tauri-backend.d.ts +15 -0
  128. package/dist/platform/tauri-backend.js +58 -0
  129. package/dist/primitives/.gitkeep +0 -0
  130. package/dist/primitives/ResizableSplitter.svelte +333 -0
  131. package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
  132. package/dist/primitives/TabbedPanel.svelte +305 -0
  133. package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
  134. package/dist/primitives/base.css +42 -0
  135. package/dist/registry/client.d.ts +74 -0
  136. package/dist/registry/client.js +117 -0
  137. package/dist/registry/index.d.ts +13 -0
  138. package/dist/registry/index.js +14 -0
  139. package/dist/registry/installer.d.ts +53 -0
  140. package/dist/registry/installer.js +168 -0
  141. package/dist/registry/integrity.d.ts +32 -0
  142. package/dist/registry/integrity.js +92 -0
  143. package/dist/registry/loader.d.ts +50 -0
  144. package/dist/registry/loader.js +145 -0
  145. package/dist/registry/schema.d.ts +47 -0
  146. package/dist/registry/schema.js +185 -0
  147. package/dist/registry/storage.d.ts +37 -0
  148. package/dist/registry/storage.js +101 -0
  149. package/dist/registry/types.d.ts +262 -0
  150. package/dist/registry/types.js +14 -0
  151. package/dist/server-shard/types.d.ts +67 -0
  152. package/dist/server-shard/types.js +13 -0
  153. package/dist/sh3core-shard/ShellHome.svelte +192 -0
  154. package/dist/sh3core-shard/ShellHome.svelte.d.ts +3 -0
  155. package/dist/sh3core-shard/ShellTitle.svelte +171 -0
  156. package/dist/sh3core-shard/ShellTitle.svelte.d.ts +3 -0
  157. package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +2 -0
  158. package/dist/sh3core-shard/sh3coreShard.svelte.js +53 -0
  159. package/dist/shards/activate.svelte.d.ts +52 -0
  160. package/dist/shards/activate.svelte.js +186 -0
  161. package/dist/shards/registry.d.ts +4 -0
  162. package/dist/shards/registry.js +28 -0
  163. package/dist/shards/types.d.ts +207 -0
  164. package/dist/shards/types.js +20 -0
  165. package/dist/shell-shard/InputLine.svelte +133 -0
  166. package/dist/shell-shard/InputLine.svelte.d.ts +11 -0
  167. package/dist/shell-shard/ScrollbackView.svelte +47 -0
  168. package/dist/shell-shard/ScrollbackView.svelte.d.ts +7 -0
  169. package/dist/shell-shard/Terminal.svelte +122 -0
  170. package/dist/shell-shard/Terminal.svelte.d.ts +8 -0
  171. package/dist/shell-shard/entries/PromptEntry.svelte +25 -0
  172. package/dist/shell-shard/entries/PromptEntry.svelte.d.ts +7 -0
  173. package/dist/shell-shard/entries/RichEntry.svelte +19 -0
  174. package/dist/shell-shard/entries/RichEntry.svelte.d.ts +8 -0
  175. package/dist/shell-shard/entries/StatusEntry.svelte +22 -0
  176. package/dist/shell-shard/entries/StatusEntry.svelte.d.ts +7 -0
  177. package/dist/shell-shard/entries/TextEntry.svelte +25 -0
  178. package/dist/shell-shard/entries/TextEntry.svelte.d.ts +7 -0
  179. package/dist/shell-shard/manifest.d.ts +2 -0
  180. package/dist/shell-shard/manifest.js +11 -0
  181. package/dist/shell-shard/protocol.d.ts +90 -0
  182. package/dist/shell-shard/protocol.js +11 -0
  183. package/dist/shell-shard/registry.d.ts +69 -0
  184. package/dist/shell-shard/registry.js +47 -0
  185. package/dist/shell-shard/rich/AppCard.svelte +25 -0
  186. package/dist/shell-shard/rich/AppCard.svelte.d.ts +10 -0
  187. package/dist/shell-shard/rich/AppsTable.svelte +29 -0
  188. package/dist/shell-shard/rich/AppsTable.svelte.d.ts +12 -0
  189. package/dist/shell-shard/rich/EnvTable.svelte +27 -0
  190. package/dist/shell-shard/rich/EnvTable.svelte.d.ts +8 -0
  191. package/dist/shell-shard/rich/HelpTable.svelte +29 -0
  192. package/dist/shell-shard/rich/HelpTable.svelte.d.ts +12 -0
  193. package/dist/shell-shard/rich/HistoryList.svelte +37 -0
  194. package/dist/shell-shard/rich/HistoryList.svelte.d.ts +9 -0
  195. package/dist/shell-shard/rich/ShardsTable.svelte +28 -0
  196. package/dist/shell-shard/rich/ShardsTable.svelte.d.ts +12 -0
  197. package/dist/shell-shard/rich/ViewsTable.svelte +31 -0
  198. package/dist/shell-shard/rich/ViewsTable.svelte.d.ts +13 -0
  199. package/dist/shell-shard/rich/ZoneTree.svelte +19 -0
  200. package/dist/shell-shard/rich/ZoneTree.svelte.d.ts +8 -0
  201. package/dist/shell-shard/rich/ZonesTable.svelte +27 -0
  202. package/dist/shell-shard/rich/ZonesTable.svelte.d.ts +11 -0
  203. package/dist/shell-shard/scrollback.svelte.d.ts +36 -0
  204. package/dist/shell-shard/scrollback.svelte.js +43 -0
  205. package/dist/shell-shard/session-client.svelte.d.ts +23 -0
  206. package/dist/shell-shard/session-client.svelte.js +120 -0
  207. package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
  208. package/dist/shell-shard/shellShard.svelte.js +139 -0
  209. package/dist/shell-shard/verbs/apps.d.ts +3 -0
  210. package/dist/shell-shard/verbs/apps.js +50 -0
  211. package/dist/shell-shard/verbs/clear.d.ts +2 -0
  212. package/dist/shell-shard/verbs/clear.js +7 -0
  213. package/dist/shell-shard/verbs/help.d.ts +2 -0
  214. package/dist/shell-shard/verbs/help.js +21 -0
  215. package/dist/shell-shard/verbs/history.d.ts +2 -0
  216. package/dist/shell-shard/verbs/history.js +20 -0
  217. package/dist/shell-shard/verbs/index.d.ts +2 -0
  218. package/dist/shell-shard/verbs/index.js +29 -0
  219. package/dist/shell-shard/verbs/session.d.ts +5 -0
  220. package/dist/shell-shard/verbs/session.js +65 -0
  221. package/dist/shell-shard/verbs/shards.d.ts +2 -0
  222. package/dist/shell-shard/verbs/shards.js +14 -0
  223. package/dist/shell-shard/verbs/views.d.ts +4 -0
  224. package/dist/shell-shard/verbs/views.js +90 -0
  225. package/dist/shell-shard/verbs/zones.d.ts +3 -0
  226. package/dist/shell-shard/verbs/zones.js +38 -0
  227. package/dist/shellRuntime.svelte.d.ts +27 -0
  228. package/dist/shellRuntime.svelte.js +27 -0
  229. package/dist/state/backends.d.ts +26 -0
  230. package/dist/state/backends.js +99 -0
  231. package/dist/state/manage.d.ts +14 -0
  232. package/dist/state/manage.js +40 -0
  233. package/dist/state/types.d.ts +55 -0
  234. package/dist/state/types.js +17 -0
  235. package/dist/state/zones.svelte.d.ts +53 -0
  236. package/dist/state/zones.svelte.js +141 -0
  237. package/dist/theme.d.ts +28 -0
  238. package/dist/theme.js +92 -0
  239. package/dist/tokens.css +102 -0
  240. package/dist/version.d.ts +2 -0
  241. package/dist/version.js +2 -0
  242. package/package.json +60 -0
@@ -0,0 +1,218 @@
1
+ /*
2
+ * Store shard — framework-shipped shard for browsing and managing
3
+ * installed packages.
4
+ *
5
+ * Contributes two views:
6
+ * - `sh3-store:browse` — searchable/filterable catalog of available packages
7
+ * - `sh3-store:installed` — list of installed packages with uninstall
8
+ *
9
+ * Uses env state for registries (server-authoritative, admin-writable) and
10
+ * an ephemeral zone for the live catalog / installed list / loading / error state.
11
+ *
12
+ * `.svelte.ts` because mounting Svelte components requires rune access.
13
+ */
14
+ import { mount, unmount } from 'svelte';
15
+ import StoreView from './StoreView.svelte';
16
+ import InstalledView from './InstalledView.svelte';
17
+ import { fetchRegistries, fetchBundle, buildPackageMeta } from '../../registry/client';
18
+ import { installPackage, listInstalledPackages } from '../../registry/installer';
19
+ import { loadBundle, savePackage } from '../../registry/storage';
20
+ import { serverInstallPackage, fetchServerPackages } from '../../env/client';
21
+ /**
22
+ * Compare two semver-like version strings.
23
+ * Returns true only if `available` is strictly greater than `installed`.
24
+ * Compares major.minor.patch left-to-right as integers.
25
+ * Non-numeric segments are treated as 0.
26
+ */
27
+ function isNewerVersion(available, installed) {
28
+ var _a, _b;
29
+ const a = available.split('.').map((s) => parseInt(s, 10) || 0);
30
+ const b = installed.split('.').map((s) => parseInt(s, 10) || 0);
31
+ const len = Math.max(a.length, b.length);
32
+ for (let i = 0; i < len; i++) {
33
+ const av = (_a = a[i]) !== null && _a !== void 0 ? _a : 0;
34
+ const bv = (_b = b[i]) !== null && _b !== void 0 ? _b : 0;
35
+ if (av > bv)
36
+ return true;
37
+ if (av < bv)
38
+ return false;
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Module-level context set during activate(). Imported by the Svelte
44
+ * view components so they can read/write store state and trigger refreshes.
45
+ */
46
+ export let storeContext = undefined;
47
+ export const storeShard = {
48
+ manifest: {
49
+ id: 'sh3-store',
50
+ label: 'Package Store',
51
+ version: '0.2.1',
52
+ views: [
53
+ { id: 'sh3-store:browse', label: 'Store' },
54
+ { id: 'sh3-store:installed', label: 'Installed' },
55
+ ],
56
+ },
57
+ activate(ctx) {
58
+ const env = ctx.env({ registries: [] });
59
+ const state = ctx.state({
60
+ ephemeral: {
61
+ catalog: [],
62
+ installed: [],
63
+ updatable: {},
64
+ loading: false,
65
+ error: null,
66
+ },
67
+ });
68
+ function recomputeUpdatable() {
69
+ const result = {};
70
+ for (const pkg of state.ephemeral.installed) {
71
+ const catalogEntry = state.ephemeral.catalog.find((c) => c.entry.id === pkg.id);
72
+ if (catalogEntry && isNewerVersion(catalogEntry.latest.version, pkg.version)) {
73
+ result[pkg.id] = catalogEntry;
74
+ }
75
+ }
76
+ state.ephemeral.updatable = result;
77
+ }
78
+ async function refreshCatalog() {
79
+ state.ephemeral.loading = true;
80
+ state.ephemeral.error = null;
81
+ try {
82
+ const results = await fetchRegistries(env.registries);
83
+ state.ephemeral.catalog = results;
84
+ recomputeUpdatable();
85
+ }
86
+ catch (err) {
87
+ state.ephemeral.error =
88
+ err instanceof Error ? err.message : String(err);
89
+ }
90
+ finally {
91
+ state.ephemeral.loading = false;
92
+ }
93
+ }
94
+ async function refreshInstalled() {
95
+ try {
96
+ const serverPkgs = await fetchServerPackages();
97
+ state.ephemeral.installed = serverPkgs.map((p) => {
98
+ var _a, _b, _c;
99
+ return ({
100
+ id: p.id,
101
+ type: p.type,
102
+ version: p.version,
103
+ sourceRegistry: (_a = p.sourceRegistry) !== null && _a !== void 0 ? _a : '',
104
+ contractVersion: (_b = p.contractVersion) !== null && _b !== void 0 ? _b : '',
105
+ installedAt: (_c = p.installedAt) !== null && _c !== void 0 ? _c : '',
106
+ });
107
+ });
108
+ recomputeUpdatable();
109
+ }
110
+ catch (err) {
111
+ console.warn('[sh3-store] Failed to list installed packages:', err instanceof Error ? err.message : err);
112
+ // Fall back to local list.
113
+ try {
114
+ const packages = await listInstalledPackages();
115
+ state.ephemeral.installed = packages;
116
+ recomputeUpdatable();
117
+ }
118
+ catch (_a) {
119
+ // Nothing to show.
120
+ }
121
+ }
122
+ }
123
+ async function addRegistry(url) {
124
+ const registries = [...env.registries];
125
+ if (registries.includes(url))
126
+ return;
127
+ registries.push(url);
128
+ await ctx.envUpdate({ registries });
129
+ }
130
+ async function removeRegistry(url) {
131
+ const registries = env.registries.filter((r) => r !== url);
132
+ await ctx.envUpdate({ registries });
133
+ }
134
+ async function updatePackage(id) {
135
+ var _a, _b;
136
+ const catalogEntry = state.ephemeral.updatable[id];
137
+ if (!catalogEntry)
138
+ return;
139
+ const installedRecord = state.ephemeral.installed.find((p) => p.id === id);
140
+ if (!installedRecord)
141
+ return;
142
+ // 1. Fetch new bundle.
143
+ const bundle = await fetchBundle(catalogEntry.latest, catalogEntry.sourceRegistry);
144
+ const meta = buildPackageMeta(catalogEntry, catalogEntry.latest);
145
+ // 2. Snapshot current state for rollback.
146
+ const oldBundle = await loadBundle(id);
147
+ const oldRecord = Object.assign({}, installedRecord);
148
+ // 3. Push to server.
149
+ const manifest = {
150
+ id: meta.id,
151
+ type: meta.type,
152
+ label: catalogEntry.entry.label,
153
+ version: meta.version,
154
+ contractVersion: meta.contractVersion,
155
+ sourceRegistry: meta.sourceRegistry,
156
+ installedAt: new Date().toISOString(),
157
+ };
158
+ const serverResult = await serverInstallPackage(manifest, bundle);
159
+ if (!serverResult.ok) {
160
+ throw new Error((_a = serverResult.error) !== null && _a !== void 0 ? _a : 'Server update failed');
161
+ }
162
+ // 4. Install locally (overwrites IndexedDB + re-registers).
163
+ const result = await installPackage(bundle, meta);
164
+ if (!result.success) {
165
+ // Rollback: restore old bundle and metadata.
166
+ if (oldBundle) {
167
+ try {
168
+ await savePackage(id, oldBundle, oldRecord);
169
+ }
170
+ catch (rollbackErr) {
171
+ console.warn(`[sh3-store] Rollback failed for "${id}":`, rollbackErr instanceof Error ? rollbackErr.message : rollbackErr);
172
+ }
173
+ }
174
+ throw new Error((_b = result.error) !== null && _b !== void 0 ? _b : 'Local install failed during update');
175
+ }
176
+ await refreshInstalled();
177
+ }
178
+ storeContext = {
179
+ env,
180
+ state,
181
+ get isAdmin() { return ctx.isAdmin; },
182
+ refreshCatalog,
183
+ refreshInstalled,
184
+ updatePackage,
185
+ addRegistry,
186
+ removeRegistry,
187
+ };
188
+ // --- View factories ---
189
+ const browseFactory = {
190
+ mount(container, _context) {
191
+ const instance = mount(StoreView, { target: container });
192
+ return {
193
+ unmount() {
194
+ unmount(instance);
195
+ },
196
+ };
197
+ },
198
+ };
199
+ const installedFactory = {
200
+ mount(container, _context) {
201
+ const instance = mount(InstalledView, { target: container });
202
+ return {
203
+ unmount() {
204
+ unmount(instance);
205
+ },
206
+ };
207
+ },
208
+ };
209
+ ctx.registerView('sh3-store:browse', browseFactory);
210
+ ctx.registerView('sh3-store:installed', installedFactory);
211
+ // refreshInstalled can run immediately (hits server, no env needed).
212
+ refreshInstalled();
213
+ },
214
+ autostart() {
215
+ // Runs after env hydration, so registries are populated.
216
+ storeContext.refreshCatalog();
217
+ },
218
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Read the id of the last-launched app from the user state zone. Used at
3
+ * boot to decide whether to auto-launch. Returns null if the user last
4
+ * returned to home or no app has ever been launched.
5
+ */
6
+ export declare function readLastApp(): string | null;
7
+ /**
8
+ * Launch an app by id. Activates all required shards (idempotent for
9
+ * already-active shards), attaches the app's layout, calls `App.activate`,
10
+ * and switches the rendered root to the app tree.
11
+ *
12
+ * If a different app is already active, it is unloaded first. Launching the
13
+ * same app that is already active is a no-op on shards — it only switches
14
+ * back from the home view if needed.
15
+ *
16
+ * @param id - The `AppManifest.id` of the app to launch. Must be registered.
17
+ * @throws If the app is not registered or a required shard is not registered.
18
+ */
19
+ export declare function launchApp(id: string): Promise<void>;
20
+ /**
21
+ * Unload an active app. Calls `App.deactivate`, detaches the layout, and
22
+ * deactivates the app's non-self-starting required shards. Switches the
23
+ * rendered root to home. No-op if the app is not currently active.
24
+ *
25
+ * @param id - The `AppManifest.id` of the app to unload.
26
+ */
27
+ export declare function unloadApp(id: string): void;
28
+ /**
29
+ * Return to the shell home view without unloading the active app. The
30
+ * app's shards stay running, its layout proxy stays attached with its
31
+ * refcount hold intact, and its view containers stay alive in the pool.
32
+ * Launching the same app again is a root swap only.
33
+ *
34
+ * Fires `suspend` hooks on all required shards (in order), then on the
35
+ * app itself. Any hook returning `false` cancels navigation — the user
36
+ * stays in the app. Returns `true` if navigation succeeded, `false` if
37
+ * cancelled.
38
+ *
39
+ * Writes `null` to `__sh3core__:last-app` so reloading the page while on
40
+ * home lands on home, not on the formerly-active app.
41
+ */
42
+ export declare function returnToHome(): Promise<boolean>;
@@ -0,0 +1,184 @@
1
+ /*
2
+ * App lifecycle — launch, unload, return-to-home.
3
+ *
4
+ * All three functions operate on the shared apps registry and the layout
5
+ * manager. `launchApp` is public (shell home calls it); `unloadApp` is
6
+ * internal (called from launchApp when replacing a different app);
7
+ * `returnToHome` is public (shell UI calls it).
8
+ *
9
+ * "Last active app" is persisted in the user state zone under the
10
+ * reserved shardId `__sh3core__:last-app`. The value shape is simply
11
+ * `{ id: string | null }`. Writing happens on launch (id) and on
12
+ * return-to-home (null). Boot reads it to decide whether to auto-launch.
13
+ */
14
+ import { createStateZones } from '../state/zones.svelte';
15
+ import { activateShard, deactivateShard, getShardContext, registeredShards, } from '../shards/activate.svelte';
16
+ import { attachApp, detachApp, switchToApp, switchToHome, } from '../layout/store.svelte';
17
+ import { activeApp, getRegisteredApp } from './registry.svelte';
18
+ import { createZoneManager } from '../state/manage';
19
+ import { PERMISSION_STATE_MANAGE } from '../state/types';
20
+ // ---------- last-active-app user zone ------------------------------------
21
+ /**
22
+ * Framework-reserved user-zone slot storing which app to boot into on
23
+ * the next session. Keyed under `__sh3core__:last-app` to avoid collision
24
+ * with any real shard. Reading/writing uses the same zones machinery as
25
+ * any shard — nothing special.
26
+ */
27
+ const lastAppState = createStateZones('__sh3core__:last-app', {
28
+ user: { id: null },
29
+ });
30
+ /**
31
+ * Read the id of the last-launched app from the user state zone. Used at
32
+ * boot to decide whether to auto-launch. Returns null if the user last
33
+ * returned to home or no app has ever been launched.
34
+ */
35
+ export function readLastApp() {
36
+ return lastAppState.user.id;
37
+ }
38
+ function writeLastApp(id) {
39
+ lastAppState.user.id = id;
40
+ }
41
+ // ---------- app-context state factories ----------------------------------
42
+ const appContexts = new Map();
43
+ function getOrCreateAppContext(appId) {
44
+ var _a;
45
+ let ctx = appContexts.get(appId);
46
+ if (!ctx) {
47
+ const app = getRegisteredApp(appId);
48
+ ctx = {
49
+ state: (schema) => createStateZones(`__app__:${appId}`, schema),
50
+ zones: ((_a = app === null || app === void 0 ? void 0 : app.manifest.permissions) === null || _a === void 0 ? void 0 : _a.includes(PERMISSION_STATE_MANAGE))
51
+ ? createZoneManager()
52
+ : undefined,
53
+ };
54
+ appContexts.set(appId, ctx);
55
+ }
56
+ return ctx;
57
+ }
58
+ // ---------- launch --------------------------------------------------------
59
+ /**
60
+ * Launch an app by id. Activates all required shards (idempotent for
61
+ * already-active shards), attaches the app's layout, calls `App.activate`,
62
+ * and switches the rendered root to the app tree.
63
+ *
64
+ * If a different app is already active, it is unloaded first. Launching the
65
+ * same app that is already active is a no-op on shards — it only switches
66
+ * back from the home view if needed.
67
+ *
68
+ * @param id - The `AppManifest.id` of the app to launch. Must be registered.
69
+ * @throws If the app is not registered or a required shard is not registered.
70
+ */
71
+ export async function launchApp(id) {
72
+ var _a, _b, _c;
73
+ const app = getRegisteredApp(id);
74
+ if (!app) {
75
+ throw new Error(`Cannot launch app "${id}": not registered`);
76
+ }
77
+ // If a different app is already active, unload it first. Relaunching
78
+ // the same app (while it's active) is a no-op on shards and layout;
79
+ // we only swap the rendered root back to 'app' in case the user was
80
+ // on home.
81
+ if (activeApp.id && activeApp.id !== id) {
82
+ unloadApp(activeApp.id);
83
+ }
84
+ else if (activeApp.id === id) {
85
+ // Re-entering the same app from Home — fire resume hooks.
86
+ for (const shardId of app.manifest.requiredShards) {
87
+ const shard = registeredShards.get(shardId);
88
+ const shardCtx = getShardContext(shardId);
89
+ if (shard && shardCtx)
90
+ void ((_a = shard.resume) === null || _a === void 0 ? void 0 : _a.call(shard, shardCtx));
91
+ }
92
+ void ((_b = app.resume) === null || _b === void 0 ? void 0 : _b.call(app, getOrCreateAppContext(id)));
93
+ switchToApp();
94
+ writeLastApp(id);
95
+ return;
96
+ }
97
+ // Activate every required shard. `activateShard` is idempotent for
98
+ // already-active shards (self-starting shards, or shards shared with
99
+ // a previous app that stayed resident) so this is safe to call
100
+ // unconditionally.
101
+ for (const shardId of app.manifest.requiredShards) {
102
+ if (!registeredShards.has(shardId)) {
103
+ throw new Error(`App "${id}" requires shard "${shardId}" which is not registered`);
104
+ }
105
+ await activateShard(shardId);
106
+ }
107
+ // Attach the layout (creates the workspace-zone proxy with version
108
+ // gate) and run the app's optional activate hook.
109
+ attachApp(app);
110
+ void ((_c = app.activate) === null || _c === void 0 ? void 0 : _c.call(app, getOrCreateAppContext(id)));
111
+ activeApp.id = id;
112
+ switchToApp();
113
+ writeLastApp(id);
114
+ }
115
+ // ---------- unload --------------------------------------------------------
116
+ /**
117
+ * Unload an active app. Calls `App.deactivate`, detaches the layout, and
118
+ * deactivates the app's non-self-starting required shards. Switches the
119
+ * rendered root to home. No-op if the app is not currently active.
120
+ *
121
+ * @param id - The `AppManifest.id` of the app to unload.
122
+ */
123
+ export function unloadApp(id) {
124
+ var _a;
125
+ if (activeApp.id !== id)
126
+ return;
127
+ const app = getRegisteredApp(id);
128
+ if (!app)
129
+ return;
130
+ void ((_a = app.deactivate) === null || _a === void 0 ? void 0 : _a.call(app));
131
+ // Detach layout (releases the refcount holds; pool cleanup runs on
132
+ // the next microtask for any slots that no longer have a renderer).
133
+ // Switch to home first so LayoutRenderer stops reading the app's
134
+ // tree before detachApp drops its references.
135
+ switchToHome();
136
+ detachApp();
137
+ // Deactivate this app's required shards IF no other consumer needs
138
+ // them. Phase 8 has at most one app active at a time, so "no other
139
+ // consumer" reduces to "not self-starting AND not required by any
140
+ // other registered app that happens to already be active" — but we
141
+ // don't run multiple apps, so the only survivors are self-starters.
142
+ // The simple rule: deactivate a required shard unless it was
143
+ // self-starting (has an `autostart` field defined).
144
+ for (const shardId of app.manifest.requiredShards) {
145
+ const shard = registeredShards.get(shardId);
146
+ if (!shard)
147
+ continue;
148
+ if (shard.autostart)
149
+ continue; // self-starter stays running
150
+ deactivateShard(shardId);
151
+ }
152
+ activeApp.id = null;
153
+ appContexts.delete(id);
154
+ }
155
+ // ---------- return to home -----------------------------------------------
156
+ /**
157
+ * Return to the shell home view without unloading the active app. The
158
+ * app's shards stay running, its layout proxy stays attached with its
159
+ * refcount hold intact, and its view containers stay alive in the pool.
160
+ * Launching the same app again is a root swap only.
161
+ *
162
+ * Fires `suspend` hooks on all required shards (in order), then on the
163
+ * app itself. Any hook returning `false` cancels navigation — the user
164
+ * stays in the app. Returns `true` if navigation succeeded, `false` if
165
+ * cancelled.
166
+ *
167
+ * Writes `null` to `__sh3core__:last-app` so reloading the page while on
168
+ * home lands on home, not on the formerly-active app.
169
+ */
170
+ export async function returnToHome() {
171
+ const app = activeApp.id ? getRegisteredApp(activeApp.id) : null;
172
+ if (app) {
173
+ for (const shardId of app.manifest.requiredShards) {
174
+ const shard = registeredShards.get(shardId);
175
+ if ((shard === null || shard === void 0 ? void 0 : shard.suspend) && (await shard.suspend()) === false)
176
+ return false;
177
+ }
178
+ if (app.suspend && (await app.suspend()) === false)
179
+ return false;
180
+ }
181
+ switchToHome();
182
+ writeLastApp(null);
183
+ return true;
184
+ }
@@ -0,0 +1,40 @@
1
+ import type { App, AppManifest } from './types';
2
+ /**
3
+ * Reactive map of all registered apps keyed by `AppManifest.id`. Populated
4
+ * once at boot by the host's glob-discovery loop via `registerApp`. A future
5
+ * runtime loader may append entries at runtime.
6
+ */
7
+ export declare const registeredApps: Map<string, App>;
8
+ /**
9
+ * Reactive slot tracking the currently-active app id. Null when the shell is
10
+ * showing the home screen or no app has been launched yet. Phase 8 allows at
11
+ * most one active app at a time.
12
+ */
13
+ export declare const activeApp: {
14
+ id: string | null;
15
+ };
16
+ /**
17
+ * Register (or re-register) an app with the framework.
18
+ *
19
+ * If an app with the same id already exists it is silently replaced,
20
+ * which is the expected path during package updates — the new bundle is
21
+ * loaded and re-registered without requiring a full page reload.
22
+ *
23
+ * @param app - The app module to register.
24
+ */
25
+ export declare function registerApp(app: App): void;
26
+ /**
27
+ * Reactive snapshot of all registered app manifests. Shell home iterates
28
+ * this to populate its launcher list. Returns only manifests so callers
29
+ * don't reach into app bodies (initialLayout, activate hook) — those are
30
+ * launch-time concerns.
31
+ */
32
+ export declare function listRegisteredApps(): AppManifest[];
33
+ /** Reactive current-app manifest (or null when none is active). */
34
+ export declare function getActiveApp(): AppManifest | null;
35
+ /**
36
+ * Lookup for framework-internal use — returns the full App (not just the
37
+ * manifest) so lifecycle code can reach into `initialLayout` and the
38
+ * activate hook. Not re-exported through `api.ts`.
39
+ */
40
+ export declare function getRegisteredApp(id: string): App | undefined;
@@ -0,0 +1,59 @@
1
+ /*
2
+ * App registry — reactive record of every registered app, plus the
3
+ * single slot for the currently-active app.
4
+ *
5
+ * Registration is called from the host (main.ts glob loop or a future
6
+ * runtime loader). The shell home view reads `listRegisteredApps()`
7
+ * reactively so newly-registered apps appear in the list without
8
+ * reboot. `activeApp` tracks which app (if any) is currently launched;
9
+ * phase 8 allows at most one.
10
+ */
11
+ /**
12
+ * Reactive map of all registered apps keyed by `AppManifest.id`. Populated
13
+ * once at boot by the host's glob-discovery loop via `registerApp`. A future
14
+ * runtime loader may append entries at runtime.
15
+ */
16
+ export const registeredApps = $state(new Map());
17
+ /**
18
+ * Reactive slot tracking the currently-active app id. Null when the shell is
19
+ * showing the home screen or no app has been launched yet. Phase 8 allows at
20
+ * most one active app at a time.
21
+ */
22
+ export const activeApp = $state({ id: null });
23
+ /**
24
+ * Register (or re-register) an app with the framework.
25
+ *
26
+ * If an app with the same id already exists it is silently replaced,
27
+ * which is the expected path during package updates — the new bundle is
28
+ * loaded and re-registered without requiring a full page reload.
29
+ *
30
+ * @param app - The app module to register.
31
+ */
32
+ export function registerApp(app) {
33
+ registeredApps.set(app.manifest.id, app);
34
+ }
35
+ /**
36
+ * Reactive snapshot of all registered app manifests. Shell home iterates
37
+ * this to populate its launcher list. Returns only manifests so callers
38
+ * don't reach into app bodies (initialLayout, activate hook) — those are
39
+ * launch-time concerns.
40
+ */
41
+ export function listRegisteredApps() {
42
+ return Array.from(registeredApps.values()).map((a) => a.manifest);
43
+ }
44
+ /** Reactive current-app manifest (or null when none is active). */
45
+ export function getActiveApp() {
46
+ var _a, _b;
47
+ const id = activeApp.id;
48
+ if (!id)
49
+ return null;
50
+ return (_b = (_a = registeredApps.get(id)) === null || _a === void 0 ? void 0 : _a.manifest) !== null && _b !== void 0 ? _b : null;
51
+ }
52
+ /**
53
+ * Lookup for framework-internal use — returns the full App (not just the
54
+ * manifest) so lifecycle code can reach into `initialLayout` and the
55
+ * activate hook. Not re-exported through `api.ts`.
56
+ */
57
+ export function getRegisteredApp(id) {
58
+ return registeredApps.get(id);
59
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Terminal app manifest.
3
+ *
4
+ * Framework-shipped: registered in host.ts during bootstrap.
5
+ * Requires the `shell` shard which provides the `shell:terminal` view.
6
+ */
7
+ import type { AppManifest } from '../../apps/types';
8
+ export declare const manifest: AppManifest;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Terminal app manifest.
3
+ *
4
+ * Framework-shipped: registered in host.ts during bootstrap.
5
+ * Requires the `shell` shard which provides the `shell:terminal` view.
6
+ */
7
+ export const manifest = {
8
+ id: 'terminal',
9
+ label: 'Terminal',
10
+ version: '0.1.0',
11
+ requiredShards: ['shell'],
12
+ layoutVersion: 1,
13
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Built-in Terminal app — mounts the `shell:terminal` view in a single slot.
3
+ *
4
+ * Framework-shipped: registered in host.ts during bootstrap.
5
+ */
6
+ import type { App } from '../../apps/types';
7
+ export declare const terminalApp: App;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Built-in Terminal app — mounts the `shell:terminal` view in a single slot.
3
+ *
4
+ * Framework-shipped: registered in host.ts during bootstrap.
5
+ */
6
+ import { manifest } from './manifest';
7
+ export const terminalApp = {
8
+ manifest,
9
+ initialLayout: {
10
+ type: 'slot',
11
+ slotId: 'terminal.main',
12
+ viewId: 'shell:terminal',
13
+ },
14
+ };