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.
- package/README.md +9 -0
- package/dist/Shell.svelte +283 -0
- package/dist/Shell.svelte.d.ts +5 -0
- package/dist/api.d.ts +28 -0
- package/dist/api.js +50 -0
- package/dist/app/admin/ApiKeysView.svelte +169 -0
- package/dist/app/admin/ApiKeysView.svelte.d.ts +3 -0
- package/dist/app/admin/AuthSettingsView.svelte +105 -0
- package/dist/app/admin/AuthSettingsView.svelte.d.ts +3 -0
- package/dist/app/admin/SystemView.svelte +73 -0
- package/dist/app/admin/SystemView.svelte.d.ts +3 -0
- package/dist/app/admin/UsersView.svelte +188 -0
- package/dist/app/admin/UsersView.svelte.d.ts +3 -0
- package/dist/app/admin/adminApp.d.ts +7 -0
- package/dist/app/admin/adminApp.js +25 -0
- package/dist/app/admin/adminShard.svelte.d.ts +4 -0
- package/dist/app/admin/adminShard.svelte.js +62 -0
- package/dist/app/store/InstalledView.svelte +246 -0
- package/dist/app/store/InstalledView.svelte.d.ts +3 -0
- package/dist/app/store/StoreView.svelte +522 -0
- package/dist/app/store/StoreView.svelte.d.ts +3 -0
- package/dist/app/store/storeApp.d.ts +10 -0
- package/dist/app/store/storeApp.js +26 -0
- package/dist/app/store/storeShard.svelte.d.ts +38 -0
- package/dist/app/store/storeShard.svelte.js +218 -0
- package/dist/apps/lifecycle.d.ts +42 -0
- package/dist/apps/lifecycle.js +184 -0
- package/dist/apps/registry.svelte.d.ts +40 -0
- package/dist/apps/registry.svelte.js +59 -0
- package/dist/apps/terminal/manifest.d.ts +8 -0
- package/dist/apps/terminal/manifest.js +13 -0
- package/dist/apps/terminal/terminal-app.d.ts +7 -0
- package/dist/apps/terminal/terminal-app.js +14 -0
- package/dist/apps/types.d.ts +93 -0
- package/dist/apps/types.js +10 -0
- package/dist/artifact.d.ts +32 -0
- package/dist/artifact.js +1 -0
- package/dist/assets/SH3.png +0 -0
- package/dist/assets/icons.svg +1126 -0
- package/dist/assets.d.ts +13 -0
- package/dist/auth/GuestBanner.svelte +134 -0
- package/dist/auth/GuestBanner.svelte.d.ts +3 -0
- package/dist/auth/SignInWall.svelte +203 -0
- package/dist/auth/SignInWall.svelte.d.ts +7 -0
- package/dist/auth/auth.svelte.d.ts +69 -0
- package/dist/auth/auth.svelte.js +165 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/types.d.ts +41 -0
- package/dist/auth/types.js +6 -0
- package/dist/build.d.ts +49 -0
- package/dist/build.js +236 -0
- package/dist/contract.d.ts +20 -0
- package/dist/contract.js +28 -0
- package/dist/createShell.d.ts +24 -0
- package/dist/createShell.js +131 -0
- package/dist/documents/backends.d.ts +17 -0
- package/dist/documents/backends.js +156 -0
- package/dist/documents/config.d.ts +7 -0
- package/dist/documents/config.js +27 -0
- package/dist/documents/handle.d.ts +6 -0
- package/dist/documents/handle.js +154 -0
- package/dist/documents/http-backend.d.ts +22 -0
- package/dist/documents/http-backend.js +78 -0
- package/dist/documents/index.d.ts +6 -0
- package/dist/documents/index.js +8 -0
- package/dist/documents/notifications.d.ts +9 -0
- package/dist/documents/notifications.js +39 -0
- package/dist/documents/types.d.ts +97 -0
- package/dist/documents/types.js +12 -0
- package/dist/env/client.d.ts +44 -0
- package/dist/env/client.js +106 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +1 -0
- package/dist/env/types.d.ts +12 -0
- package/dist/env/types.js +8 -0
- package/dist/host-entry.d.ts +13 -0
- package/dist/host-entry.js +17 -0
- package/dist/host.d.ts +15 -0
- package/dist/host.js +86 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +14 -0
- package/dist/layout/DragPreview.svelte +63 -0
- package/dist/layout/DragPreview.svelte.d.ts +3 -0
- package/dist/layout/LayoutRenderer.svelte +262 -0
- package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
- package/dist/layout/SlotContainer.svelte +140 -0
- package/dist/layout/SlotContainer.svelte.d.ts +8 -0
- package/dist/layout/SlotDropZone.svelte +122 -0
- package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
- package/dist/layout/drag.svelte.d.ts +45 -0
- package/dist/layout/drag.svelte.js +200 -0
- package/dist/layout/inspection.d.ts +72 -0
- package/dist/layout/inspection.js +209 -0
- package/dist/layout/ops.d.ts +100 -0
- package/dist/layout/ops.js +310 -0
- package/dist/layout/slotHostPool.svelte.d.ts +36 -0
- package/dist/layout/slotHostPool.svelte.js +229 -0
- package/dist/layout/store.svelte.d.ts +39 -0
- package/dist/layout/store.svelte.js +153 -0
- package/dist/layout/tree-walk.d.ts +15 -0
- package/dist/layout/tree-walk.js +33 -0
- package/dist/layout/types.d.ts +108 -0
- package/dist/layout/types.js +25 -0
- package/dist/migrations/shell-rename.d.ts +16 -0
- package/dist/migrations/shell-rename.js +48 -0
- package/dist/overlays/ModalFrame.svelte +87 -0
- package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
- package/dist/overlays/PopupFrame.svelte +85 -0
- package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
- package/dist/overlays/ToastItem.svelte +77 -0
- package/dist/overlays/ToastItem.svelte.d.ts +9 -0
- package/dist/overlays/focusTrap.d.ts +1 -0
- package/dist/overlays/focusTrap.js +64 -0
- package/dist/overlays/modal.d.ts +9 -0
- package/dist/overlays/modal.js +141 -0
- package/dist/overlays/popup.d.ts +9 -0
- package/dist/overlays/popup.js +108 -0
- package/dist/overlays/roots.d.ts +4 -0
- package/dist/overlays/roots.js +31 -0
- package/dist/overlays/toast.d.ts +6 -0
- package/dist/overlays/toast.js +93 -0
- package/dist/overlays/types.d.ts +31 -0
- package/dist/overlays/types.js +15 -0
- package/dist/platform/index.d.ts +10 -0
- package/dist/platform/index.js +33 -0
- package/dist/platform/tauri-backend.d.ts +15 -0
- package/dist/platform/tauri-backend.js +58 -0
- package/dist/primitives/.gitkeep +0 -0
- package/dist/primitives/ResizableSplitter.svelte +333 -0
- package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
- package/dist/primitives/TabbedPanel.svelte +305 -0
- package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
- package/dist/primitives/base.css +42 -0
- package/dist/registry/client.d.ts +74 -0
- package/dist/registry/client.js +117 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.js +14 -0
- package/dist/registry/installer.d.ts +53 -0
- package/dist/registry/installer.js +168 -0
- package/dist/registry/integrity.d.ts +32 -0
- package/dist/registry/integrity.js +92 -0
- package/dist/registry/loader.d.ts +50 -0
- package/dist/registry/loader.js +145 -0
- package/dist/registry/schema.d.ts +47 -0
- package/dist/registry/schema.js +185 -0
- package/dist/registry/storage.d.ts +37 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/types.d.ts +262 -0
- package/dist/registry/types.js +14 -0
- package/dist/server-shard/types.d.ts +67 -0
- package/dist/server-shard/types.js +13 -0
- package/dist/sh3core-shard/ShellHome.svelte +192 -0
- package/dist/sh3core-shard/ShellHome.svelte.d.ts +3 -0
- package/dist/sh3core-shard/ShellTitle.svelte +171 -0
- package/dist/sh3core-shard/ShellTitle.svelte.d.ts +3 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +2 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.js +53 -0
- package/dist/shards/activate.svelte.d.ts +52 -0
- package/dist/shards/activate.svelte.js +186 -0
- package/dist/shards/registry.d.ts +4 -0
- package/dist/shards/registry.js +28 -0
- package/dist/shards/types.d.ts +207 -0
- package/dist/shards/types.js +20 -0
- package/dist/shell-shard/InputLine.svelte +133 -0
- package/dist/shell-shard/InputLine.svelte.d.ts +11 -0
- package/dist/shell-shard/ScrollbackView.svelte +47 -0
- package/dist/shell-shard/ScrollbackView.svelte.d.ts +7 -0
- package/dist/shell-shard/Terminal.svelte +122 -0
- package/dist/shell-shard/Terminal.svelte.d.ts +8 -0
- package/dist/shell-shard/entries/PromptEntry.svelte +25 -0
- package/dist/shell-shard/entries/PromptEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/entries/RichEntry.svelte +19 -0
- package/dist/shell-shard/entries/RichEntry.svelte.d.ts +8 -0
- package/dist/shell-shard/entries/StatusEntry.svelte +22 -0
- package/dist/shell-shard/entries/StatusEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/entries/TextEntry.svelte +25 -0
- package/dist/shell-shard/entries/TextEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/manifest.d.ts +2 -0
- package/dist/shell-shard/manifest.js +11 -0
- package/dist/shell-shard/protocol.d.ts +90 -0
- package/dist/shell-shard/protocol.js +11 -0
- package/dist/shell-shard/registry.d.ts +69 -0
- package/dist/shell-shard/registry.js +47 -0
- package/dist/shell-shard/rich/AppCard.svelte +25 -0
- package/dist/shell-shard/rich/AppCard.svelte.d.ts +10 -0
- package/dist/shell-shard/rich/AppsTable.svelte +29 -0
- package/dist/shell-shard/rich/AppsTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/EnvTable.svelte +27 -0
- package/dist/shell-shard/rich/EnvTable.svelte.d.ts +8 -0
- package/dist/shell-shard/rich/HelpTable.svelte +29 -0
- package/dist/shell-shard/rich/HelpTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/HistoryList.svelte +37 -0
- package/dist/shell-shard/rich/HistoryList.svelte.d.ts +9 -0
- package/dist/shell-shard/rich/ShardsTable.svelte +28 -0
- package/dist/shell-shard/rich/ShardsTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/ViewsTable.svelte +31 -0
- package/dist/shell-shard/rich/ViewsTable.svelte.d.ts +13 -0
- package/dist/shell-shard/rich/ZoneTree.svelte +19 -0
- package/dist/shell-shard/rich/ZoneTree.svelte.d.ts +8 -0
- package/dist/shell-shard/rich/ZonesTable.svelte +27 -0
- package/dist/shell-shard/rich/ZonesTable.svelte.d.ts +11 -0
- package/dist/shell-shard/scrollback.svelte.d.ts +36 -0
- package/dist/shell-shard/scrollback.svelte.js +43 -0
- package/dist/shell-shard/session-client.svelte.d.ts +23 -0
- package/dist/shell-shard/session-client.svelte.js +120 -0
- package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
- package/dist/shell-shard/shellShard.svelte.js +139 -0
- package/dist/shell-shard/verbs/apps.d.ts +3 -0
- package/dist/shell-shard/verbs/apps.js +50 -0
- package/dist/shell-shard/verbs/clear.d.ts +2 -0
- package/dist/shell-shard/verbs/clear.js +7 -0
- package/dist/shell-shard/verbs/help.d.ts +2 -0
- package/dist/shell-shard/verbs/help.js +21 -0
- package/dist/shell-shard/verbs/history.d.ts +2 -0
- package/dist/shell-shard/verbs/history.js +20 -0
- package/dist/shell-shard/verbs/index.d.ts +2 -0
- package/dist/shell-shard/verbs/index.js +29 -0
- package/dist/shell-shard/verbs/session.d.ts +5 -0
- package/dist/shell-shard/verbs/session.js +65 -0
- package/dist/shell-shard/verbs/shards.d.ts +2 -0
- package/dist/shell-shard/verbs/shards.js +14 -0
- package/dist/shell-shard/verbs/views.d.ts +4 -0
- package/dist/shell-shard/verbs/views.js +90 -0
- package/dist/shell-shard/verbs/zones.d.ts +3 -0
- package/dist/shell-shard/verbs/zones.js +38 -0
- package/dist/shellRuntime.svelte.d.ts +27 -0
- package/dist/shellRuntime.svelte.js +27 -0
- package/dist/state/backends.d.ts +26 -0
- package/dist/state/backends.js +99 -0
- package/dist/state/manage.d.ts +14 -0
- package/dist/state/manage.js +40 -0
- package/dist/state/types.d.ts +55 -0
- package/dist/state/types.js +17 -0
- package/dist/state/zones.svelte.d.ts +53 -0
- package/dist/state/zones.svelte.js +141 -0
- package/dist/theme.d.ts +28 -0
- package/dist/theme.js +92 -0
- package/dist/tokens.css +102 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- 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,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
|
+
};
|