sh3-core 0.5.4 → 0.5.6
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.
Potentially problematic release.
This version of sh3-core might be problematic. Click here for more details.
- package/dist/Shell.svelte +4 -1
- package/dist/admin/AuthSettingsView.svelte +105 -0
- package/dist/admin/AuthSettingsView.svelte.d.ts +3 -0
- package/dist/admin/SystemView.svelte +73 -0
- package/dist/admin/SystemView.svelte.d.ts +3 -0
- package/dist/admin/UsersView.svelte +189 -0
- package/dist/admin/UsersView.svelte.d.ts +3 -0
- package/dist/admin/adminApp.d.ts +7 -0
- package/dist/admin/adminApp.js +24 -0
- package/dist/admin/adminShard.svelte.d.ts +4 -0
- package/dist/admin/adminShard.svelte.js +52 -0
- package/dist/api.d.ts +2 -1
- package/dist/api.js +1 -1
- package/dist/auth/GuestBanner.svelte +144 -0
- package/dist/auth/GuestBanner.svelte.d.ts +3 -0
- package/dist/auth/SignInWall.svelte +213 -0
- package/dist/auth/SignInWall.svelte.d.ts +8 -0
- package/dist/auth/auth.svelte.d.ts +42 -31
- package/dist/auth/auth.svelte.js +106 -89
- package/dist/auth/index.d.ts +2 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/types.d.ts +41 -0
- package/dist/auth/types.js +6 -0
- package/dist/build.js +70 -16
- package/dist/createShell.d.ts +2 -2
- package/dist/createShell.js +78 -33
- package/dist/host-entry.d.ts +2 -1
- package/dist/host-entry.js +2 -2
- package/dist/host.d.ts +0 -2
- package/dist/host.js +11 -25
- package/dist/shell-shard/ShellHome.svelte +29 -115
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +2 -2
package/dist/build.js
CHANGED
|
@@ -112,7 +112,7 @@ export function sh3Artifact(options = {}) {
|
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
114
|
async closeBundle() {
|
|
115
|
-
var _a, _b, _c, _d;
|
|
115
|
+
var _a, _b, _c, _d, _e;
|
|
116
116
|
if (!entryFileName)
|
|
117
117
|
return;
|
|
118
118
|
const clientSrc = join(outDir, entryFileName);
|
|
@@ -122,7 +122,7 @@ export function sh3Artifact(options = {}) {
|
|
|
122
122
|
try {
|
|
123
123
|
source = readFileSync(clientSrc, 'utf-8');
|
|
124
124
|
}
|
|
125
|
-
catch (
|
|
125
|
+
catch (_f) {
|
|
126
126
|
console.warn('[sh3-artifact] Could not read entry chunk:', clientSrc);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
@@ -131,17 +131,71 @@ export function sh3Artifact(options = {}) {
|
|
|
131
131
|
writeFileSync(clientDest, source);
|
|
132
132
|
unlinkSync(clientSrc);
|
|
133
133
|
}
|
|
134
|
-
// --- Extract manifest fields
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const label = extract(/\blabel\s*:\s*["']([^"']+)["']/);
|
|
141
|
-
const version = extract(/\bversion\s*:\s*["']([^"']+)["']/);
|
|
142
|
-
const hasShard = /\bviews\s*:\s*\[/.test(source);
|
|
134
|
+
// --- Extract manifest fields from App or Shard block ---
|
|
135
|
+
//
|
|
136
|
+
// The bundle may contain both an App manifest (has `requiredShards`)
|
|
137
|
+
// and a Shard manifest (has `views: [`). We extract id/label/version
|
|
138
|
+
// from the App block first, then fall back to the Shard block.
|
|
139
|
+
// If neither block exists, the build fails with a clear error.
|
|
143
140
|
const hasApp = /\brequiredShards\s*:\s*\[/.test(source);
|
|
141
|
+
const hasShard = /\bviews\s*:\s*\[/.test(source);
|
|
142
|
+
if (!hasApp && !hasShard) {
|
|
143
|
+
throw new Error('[sh3-artifact] Could not find an App manifest (requiredShards) or Shard manifest (views) in the entry chunk. '
|
|
144
|
+
+ 'Ensure the entry exports an App or Shard object.');
|
|
145
|
+
}
|
|
144
146
|
const type = hasShard && hasApp ? 'combo' : hasApp ? 'app' : 'shard';
|
|
147
|
+
/**
|
|
148
|
+
* Extract id, label, and version from the manifest object block that
|
|
149
|
+
* contains `anchor`. Walks backwards from the anchor to find the
|
|
150
|
+
* opening `{`, then forward to find the matching `}`, and extracts
|
|
151
|
+
* fields from within that slice.
|
|
152
|
+
*/
|
|
153
|
+
function extractFromBlock(anchor) {
|
|
154
|
+
const anchorMatch = anchor.exec(source);
|
|
155
|
+
if (!anchorMatch)
|
|
156
|
+
return null;
|
|
157
|
+
// Walk backwards from the anchor to find the enclosing `{`.
|
|
158
|
+
let depth = 0;
|
|
159
|
+
let blockStart = anchorMatch.index;
|
|
160
|
+
for (let i = anchorMatch.index - 1; i >= 0; i--) {
|
|
161
|
+
if (source[i] === '}')
|
|
162
|
+
depth++;
|
|
163
|
+
if (source[i] === '{') {
|
|
164
|
+
if (depth === 0) {
|
|
165
|
+
blockStart = i;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
depth--;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Walk forwards from the anchor to find the matching `}`.
|
|
172
|
+
depth = 0;
|
|
173
|
+
let blockEnd = source.length;
|
|
174
|
+
for (let i = blockStart; i < source.length; i++) {
|
|
175
|
+
if (source[i] === '{')
|
|
176
|
+
depth++;
|
|
177
|
+
if (source[i] === '}') {
|
|
178
|
+
depth--;
|
|
179
|
+
if (depth === 0) {
|
|
180
|
+
blockEnd = i + 1;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const block = source.slice(blockStart, blockEnd);
|
|
186
|
+
const get = (pattern) => {
|
|
187
|
+
const m = block.match(pattern);
|
|
188
|
+
return m ? m[1] : '';
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
id: get(/\bid\s*:\s*["']([^"']+)["']/),
|
|
192
|
+
label: get(/\blabel\s*:\s*["']([^"']+)["']/),
|
|
193
|
+
version: get(/\bversion\s*:\s*["']([^"']+)["']/),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// App first, then Shard.
|
|
197
|
+
const extracted = (_a = extractFromBlock(/\brequiredShards\s*:\s*\[/)) !== null && _a !== void 0 ? _a : extractFromBlock(/\bviews\s*:\s*\[/);
|
|
198
|
+
const { id, label, version } = extracted;
|
|
145
199
|
// --- Optional server bundle ---
|
|
146
200
|
let hasServer = false;
|
|
147
201
|
if (options.serverEntry && existsSync(options.serverEntry)) {
|
|
@@ -156,13 +210,13 @@ export function sh3Artifact(options = {}) {
|
|
|
156
210
|
pkgDescription = typeof pkg.description === 'string' ? pkg.description : undefined;
|
|
157
211
|
pkgAuthor = typeof pkg.author === 'string'
|
|
158
212
|
? pkg.author
|
|
159
|
-
: typeof ((
|
|
213
|
+
: typeof ((_b = pkg.author) === null || _b === void 0 ? void 0 : _b.name) === 'string' ? pkg.author.name : undefined;
|
|
160
214
|
}
|
|
161
|
-
catch ( /* no package.json or unreadable */
|
|
215
|
+
catch ( /* no package.json or unreadable */_g) { /* no package.json or unreadable */ }
|
|
162
216
|
// --- Write manifest.json ---
|
|
163
|
-
const overrides = (
|
|
164
|
-
const finalDescription = (
|
|
165
|
-
const finalAuthor = (
|
|
217
|
+
const overrides = (_c = options.manifest) !== null && _c !== void 0 ? _c : {};
|
|
218
|
+
const finalDescription = (_d = overrides.description) !== null && _d !== void 0 ? _d : pkgDescription;
|
|
219
|
+
const finalAuthor = (_e = overrides.author) !== null && _e !== void 0 ? _e : pkgAuthor;
|
|
166
220
|
if (!finalDescription) {
|
|
167
221
|
throw new Error('[sh3-artifact] Missing "description". Add it to package.json or pass it via sh3Artifact({ manifest: { description } }).');
|
|
168
222
|
}
|
package/dist/createShell.d.ts
CHANGED
|
@@ -2,8 +2,6 @@ import type { Shard, App } from './index';
|
|
|
2
2
|
export interface ShellConfig {
|
|
3
3
|
/** Framework shard IDs to exclude (all included by default) */
|
|
4
4
|
excludeShards?: string[];
|
|
5
|
-
/** Framework app IDs to exclude (all included by default) */
|
|
6
|
-
excludeApps?: string[];
|
|
7
5
|
/** Additional shards to register */
|
|
8
6
|
shards?: Shard[];
|
|
9
7
|
/** Additional apps to register */
|
|
@@ -20,5 +18,7 @@ export interface ShellConfig {
|
|
|
20
18
|
}>;
|
|
21
19
|
/** Mount target — CSS selector or element (defaults to '#app') */
|
|
22
20
|
target?: string | HTMLElement;
|
|
21
|
+
/** Server base URL ('' for same-origin) */
|
|
22
|
+
serverUrl?: string;
|
|
23
23
|
}
|
|
24
24
|
export declare function createShell(config?: ShellConfig): Promise<void>;
|
package/dist/createShell.js
CHANGED
|
@@ -3,20 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Consumers call this from their own main.ts instead of manually
|
|
5
5
|
* importing registerShard / registerApp / bootstrap / Shell. The
|
|
6
|
-
* factory handles platform detection,
|
|
7
|
-
* mounting in the correct order.
|
|
6
|
+
* factory handles platform detection, boot config, auth gating,
|
|
7
|
+
* registration, bootstrap, and mounting in the correct order.
|
|
8
8
|
*/
|
|
9
|
-
import { mount } from 'svelte';
|
|
9
|
+
import { mount, unmount } from 'svelte';
|
|
10
10
|
import { Shell } from './index';
|
|
11
11
|
import { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner, } from './host';
|
|
12
12
|
import { resolvePlatform } from './platform/index';
|
|
13
13
|
import { hydrateTokenOverrides } from './theme';
|
|
14
14
|
import { __setEnvServerUrl } from './env/index';
|
|
15
|
+
import { __setTenantId } from './documents/config';
|
|
16
|
+
import { initFromBoot } from './auth/index';
|
|
17
|
+
import SignInWall from './auth/SignInWall.svelte';
|
|
15
18
|
export async function createShell(config) {
|
|
16
|
-
var _a, _b;
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
// auto-elevate to admin.
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
const sUrl = (_a = config === null || config === void 0 ? void 0 : config.serverUrl) !== null && _a !== void 0 ? _a : '';
|
|
21
|
+
// 1. Platform detection
|
|
20
22
|
const platform = await resolvePlatform();
|
|
21
23
|
if (platform.backends) {
|
|
22
24
|
__setBackend('workspace', platform.backends.workspace);
|
|
@@ -25,15 +27,52 @@ export async function createShell(config) {
|
|
|
25
27
|
if (platform.localOwner) {
|
|
26
28
|
setLocalOwner();
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
// this if it knows a specific URL, but this ensures env() works for
|
|
30
|
-
// same-origin deployments without explicit configuration.
|
|
31
|
-
__setEnvServerUrl('');
|
|
32
|
-
// 1c. Apply persisted theme token overrides before any component mounts,
|
|
33
|
-
// so the first frame renders with the user's chosen theme.
|
|
30
|
+
__setEnvServerUrl(sUrl);
|
|
34
31
|
hydrateTokenOverrides();
|
|
35
|
-
//
|
|
36
|
-
|
|
32
|
+
// 2. Resolve mount target early (needed for both sign-in wall and shell)
|
|
33
|
+
const target = typeof (config === null || config === void 0 ? void 0 : config.target) === 'string'
|
|
34
|
+
? document.querySelector(config.target)
|
|
35
|
+
: (_b = config === null || config === void 0 ? void 0 : config.target) !== null && _b !== void 0 ? _b : document.getElementById('app');
|
|
36
|
+
if (!target) {
|
|
37
|
+
throw new Error('SH3: mount target not found');
|
|
38
|
+
}
|
|
39
|
+
// 3. Fetch boot config (skip for local-owner platforms like Tauri/dev)
|
|
40
|
+
let bootConfig = null;
|
|
41
|
+
if (!platform.localOwner) {
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(`${sUrl}/api/boot`, { credentials: 'include' });
|
|
44
|
+
if (res.ok) {
|
|
45
|
+
bootConfig = await res.json();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (_d) {
|
|
49
|
+
// Server unreachable — boot without auth (offline mode)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 4. Auth decision point
|
|
53
|
+
if (platform.localOwner) {
|
|
54
|
+
// Local-owner (Tauri/dev): no auth, no sign-in, tenant is 'local'.
|
|
55
|
+
// setLocalOwner() already called above — admin is assumed.
|
|
56
|
+
__setTenantId('local');
|
|
57
|
+
}
|
|
58
|
+
else if (bootConfig) {
|
|
59
|
+
initFromBoot(sUrl, bootConfig);
|
|
60
|
+
__setTenantId(bootConfig.tenantId);
|
|
61
|
+
const { auth, session } = bootConfig;
|
|
62
|
+
// Hard gate: no session, auth required, no guest allowed → sign-in wall
|
|
63
|
+
if (!session && auth.required && !auth.guestAllowed) {
|
|
64
|
+
await showSignInWall(target, sUrl, bootConfig);
|
|
65
|
+
// After successful sign-in, re-fetch boot config
|
|
66
|
+
const res = await fetch(`${sUrl}/api/boot`, { credentials: 'include' });
|
|
67
|
+
if (res.ok) {
|
|
68
|
+
bootConfig = await res.json();
|
|
69
|
+
initFromBoot(sUrl, bootConfig);
|
|
70
|
+
__setTenantId(bootConfig.tenantId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// 5. Load server-discovered packages
|
|
75
|
+
if ((_c = config === null || config === void 0 ? void 0 : config.discoveredPackages) === null || _c === void 0 ? void 0 : _c.length) {
|
|
37
76
|
const { loadBundleModule } = await import('./registry/loader');
|
|
38
77
|
for (const pkg of config.discoveredPackages) {
|
|
39
78
|
try {
|
|
@@ -55,33 +94,39 @@ export async function createShell(config) {
|
|
|
55
94
|
}
|
|
56
95
|
}
|
|
57
96
|
}
|
|
58
|
-
//
|
|
59
|
-
// bootstrap() so they appear in registeredShards, but framework
|
|
60
|
-
// shards activate first (insertion-order guarantee in bootstrap).
|
|
97
|
+
// 6. Register consumer-provided shards and apps
|
|
61
98
|
if (config === null || config === void 0 ? void 0 : config.shards) {
|
|
62
|
-
for (const shard of config.shards)
|
|
99
|
+
for (const shard of config.shards)
|
|
63
100
|
registerShard(shard);
|
|
64
|
-
}
|
|
65
101
|
}
|
|
66
102
|
if (config === null || config === void 0 ? void 0 : config.apps) {
|
|
67
|
-
for (const app of config.apps)
|
|
103
|
+
for (const app of config.apps)
|
|
68
104
|
registerApp(app);
|
|
69
|
-
}
|
|
70
105
|
}
|
|
71
|
-
//
|
|
72
|
-
// filtered by the exclude lists.
|
|
106
|
+
// 7. Bootstrap
|
|
73
107
|
const bootstrapConfig = {};
|
|
74
108
|
if (config === null || config === void 0 ? void 0 : config.excludeShards)
|
|
75
109
|
bootstrapConfig.excludeShards = config.excludeShards;
|
|
76
|
-
if (config === null || config === void 0 ? void 0 : config.excludeApps)
|
|
77
|
-
bootstrapConfig.excludeApps = config.excludeApps;
|
|
78
110
|
await bootstrap(bootstrapConfig);
|
|
79
|
-
//
|
|
80
|
-
const target = typeof (config === null || config === void 0 ? void 0 : config.target) === 'string'
|
|
81
|
-
? document.querySelector(config.target)
|
|
82
|
-
: (_b = config === null || config === void 0 ? void 0 : config.target) !== null && _b !== void 0 ? _b : document.getElementById('app');
|
|
83
|
-
if (!target) {
|
|
84
|
-
throw new Error('SH3: mount target not found');
|
|
85
|
-
}
|
|
111
|
+
// 8. Mount the shell
|
|
86
112
|
mount(Shell, { target });
|
|
87
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Show the sign-in wall and wait until the user authenticates.
|
|
116
|
+
* Returns a promise that resolves after successful login.
|
|
117
|
+
*/
|
|
118
|
+
function showSignInWall(target, serverUrl, bootConfig) {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
const instance = mount(SignInWall, {
|
|
121
|
+
target,
|
|
122
|
+
props: {
|
|
123
|
+
serverUrl,
|
|
124
|
+
selfRegistration: bootConfig.auth.selfRegistration,
|
|
125
|
+
onSuccess: () => {
|
|
126
|
+
unmount(instance);
|
|
127
|
+
resolve();
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
package/dist/host-entry.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { HttpDocumentBackend } from './documents/http-backend';
|
|
|
7
7
|
export { __setEnvServerUrl } from './env/index';
|
|
8
8
|
export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
|
|
9
9
|
export type { InstalledPackage, InstallResult, PackageMeta } from './registry/types';
|
|
10
|
-
export {
|
|
10
|
+
export { initFromBoot, login, logout, register, setLocalOwner as setLocalOwnerAuth } from './auth/index';
|
|
11
|
+
export type { AuthUser, AuthSession, BootConfig, GlobalSettings } from './auth/types';
|
|
11
12
|
export { createShell } from './createShell';
|
|
12
13
|
export type { ShellConfig } from './createShell';
|
package/dist/host-entry.js
CHANGED
|
@@ -11,7 +11,7 @@ export { HttpDocumentBackend } from './documents/http-backend';
|
|
|
11
11
|
export { __setEnvServerUrl } from './env/index';
|
|
12
12
|
// Install API (host-only).
|
|
13
13
|
export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
|
|
14
|
-
//
|
|
15
|
-
export {
|
|
14
|
+
// Auth (host-only — session lifecycle, boot initialization).
|
|
15
|
+
export { initFromBoot, login, logout, register, setLocalOwner as setLocalOwnerAuth } from './auth/index';
|
|
16
16
|
// Shell boot factory.
|
|
17
17
|
export { createShell } from './createShell';
|
package/dist/host.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export { registerApp };
|
|
|
10
10
|
export interface BootstrapConfig {
|
|
11
11
|
/** Framework shard IDs to skip registration for */
|
|
12
12
|
excludeShards?: string[];
|
|
13
|
-
/** Framework app IDs to skip registration for */
|
|
14
|
-
excludeApps?: string[];
|
|
15
13
|
}
|
|
16
14
|
export declare function bootstrap(config?: BootstrapConfig): Promise<void>;
|
|
17
15
|
export { installPackage, listInstalledPackages } from './registry/installer';
|
package/dist/host.js
CHANGED
|
@@ -22,10 +22,12 @@ import { shellShard } from './shell-shard/shellShard.svelte';
|
|
|
22
22
|
import { storeShard } from './store/storeShard.svelte';
|
|
23
23
|
import { __setBackend } from './state/zones.svelte';
|
|
24
24
|
import { loadInstalledPackages } from './registry/installer';
|
|
25
|
-
import {
|
|
25
|
+
import { setLocalOwner } from './auth/index';
|
|
26
26
|
import { storeApp } from './store/storeApp';
|
|
27
27
|
import { diagnosticShard } from './diagnostic/diagnosticShard.svelte';
|
|
28
28
|
import { diagnosticApp } from './diagnostic/diagnosticApp';
|
|
29
|
+
import { adminShard } from './admin/adminShard.svelte';
|
|
30
|
+
import { adminApp } from './admin/adminApp';
|
|
29
31
|
export { __setBackend };
|
|
30
32
|
export { setLocalOwner };
|
|
31
33
|
export { __setTenantId, __setDocumentBackend } from './documents/config';
|
|
@@ -35,43 +37,27 @@ export function registerShard(shard) {
|
|
|
35
37
|
export { registerApp };
|
|
36
38
|
export async function bootstrap(config) {
|
|
37
39
|
const exShards = new Set(config === null || config === void 0 ? void 0 : config.excludeShards);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// cannot claim reserved IDs like __shell__ or sh3-store.
|
|
41
|
-
const frameworkShards = [shellShard, storeShard, diagnosticShard];
|
|
40
|
+
// 1. Framework-owned shards
|
|
41
|
+
const frameworkShards = [shellShard, storeShard, diagnosticShard, adminShard];
|
|
42
42
|
for (const shard of frameworkShards) {
|
|
43
43
|
if (!exShards.has(shard.manifest.id)) {
|
|
44
44
|
registerShardInternal(shard);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
// 2. Framework-shipped
|
|
48
|
-
const frameworkApps = [storeApp, diagnosticApp];
|
|
47
|
+
// 2. Framework-shipped apps
|
|
48
|
+
const frameworkApps = [storeApp, diagnosticApp, adminApp];
|
|
49
49
|
for (const app of frameworkApps) {
|
|
50
|
-
|
|
51
|
-
registerApp(app);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// 3. Auth — if the host already called setLocalOwner() (Tauri / dev),
|
|
55
|
-
// skip server verification. Otherwise verify the stored admin key
|
|
56
|
-
// against the server (3s timeout, fails open).
|
|
57
|
-
if (!isAdmin()) {
|
|
58
|
-
await initAuth();
|
|
50
|
+
registerApp(app);
|
|
59
51
|
}
|
|
60
|
-
//
|
|
61
|
-
// from IndexedDB. Runs after framework shards but before activation
|
|
62
|
-
// so installed packages participate in the self-starting pass.
|
|
52
|
+
// 3. Load any packages installed in a previous session from IndexedDB
|
|
63
53
|
await loadInstalledPackages();
|
|
64
|
-
//
|
|
65
|
-
// field is defined). Iteration order is insertion order, which
|
|
66
|
-
// puts __shell__ first and glob-discovered shards after — exactly
|
|
67
|
-
// what we need.
|
|
54
|
+
// 4. Activate every self-starting shard
|
|
68
55
|
for (const [id, shard] of registeredShards) {
|
|
69
56
|
if (shard.autostart) {
|
|
70
57
|
await activateShard(id);
|
|
71
58
|
}
|
|
72
59
|
}
|
|
73
|
-
//
|
|
74
|
-
// registered app, launch it; otherwise leave the shell on home.
|
|
60
|
+
// 5. Read the last-active app from the user zone
|
|
75
61
|
const lastId = readLastApp();
|
|
76
62
|
if (lastId && registeredApps.has(lastId)) {
|
|
77
63
|
await launchApp(lastId);
|
|
@@ -2,37 +2,18 @@
|
|
|
2
2
|
/*
|
|
3
3
|
* Shell home — the view shown when no app is active. Sections:
|
|
4
4
|
* 1. User apps — always visible
|
|
5
|
-
* 2. Admin apps — visible when
|
|
6
|
-
* 3. Elevate prompt — shown when not elevated
|
|
5
|
+
* 2. Admin apps — visible when user has admin role
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { listRegisteredApps, launchApp, isAdmin, VERSION } from '../api';
|
|
10
|
-
import {
|
|
9
|
+
import { logout, isAuthenticated, getUser } from '../auth/index';
|
|
11
10
|
|
|
12
11
|
const apps = $derived(listRegisteredApps());
|
|
13
12
|
const userApps = $derived(apps.filter(m => !m.admin));
|
|
14
13
|
const adminApps = $derived(apps.filter(m => m.admin));
|
|
15
14
|
const elevated = $derived(isAdmin());
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let elevateError = $state<string | null>(null);
|
|
19
|
-
let elevating = $state(false);
|
|
20
|
-
|
|
21
|
-
async function handleElevate() {
|
|
22
|
-
if (!keyInput.trim() || elevating) return;
|
|
23
|
-
elevating = true;
|
|
24
|
-
elevateError = null;
|
|
25
|
-
const ok = await elevate(keyInput.trim());
|
|
26
|
-
if (!ok) {
|
|
27
|
-
elevateError = 'Invalid API key';
|
|
28
|
-
}
|
|
29
|
-
keyInput = '';
|
|
30
|
-
elevating = false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function handleDeescalate() {
|
|
34
|
-
deescalate();
|
|
35
|
-
}
|
|
15
|
+
const authenticated = $derived(isAuthenticated());
|
|
16
|
+
const user = $derived(getUser());
|
|
36
17
|
</script>
|
|
37
18
|
|
|
38
19
|
<div class="shell-home">
|
|
@@ -40,9 +21,10 @@
|
|
|
40
21
|
<h1>SH3</h1>
|
|
41
22
|
<span class="shell-home-version">v{VERSION}</span>
|
|
42
23
|
<span class="shell-home-alpha">alpha</span>
|
|
43
|
-
{#if
|
|
44
|
-
<
|
|
45
|
-
|
|
24
|
+
{#if authenticated && user}
|
|
25
|
+
<span class="shell-home-user">{user.displayName}</span>
|
|
26
|
+
<button type="button" class="shell-home-deescalate" onclick={() => logout()}>
|
|
27
|
+
Sign out
|
|
46
28
|
</button>
|
|
47
29
|
{/if}
|
|
48
30
|
</header>
|
|
@@ -70,54 +52,26 @@
|
|
|
70
52
|
</section>
|
|
71
53
|
{/if}
|
|
72
54
|
|
|
73
|
-
{#if elevated}
|
|
74
|
-
{#if adminApps.length > 0}
|
|
75
|
-
<section class="shell-home-section">
|
|
76
|
-
<h2 class="shell-home-section-title">Admin</h2>
|
|
77
|
-
<ul class="shell-home-list">
|
|
78
|
-
{#each adminApps as manifest (manifest.id)}
|
|
79
|
-
<li class="shell-home-entry">
|
|
80
|
-
<div class="shell-home-entry-label">{manifest.label}</div>
|
|
81
|
-
<div class="shell-home-entry-meta">
|
|
82
|
-
{manifest.id} · v{manifest.version}
|
|
83
|
-
</div>
|
|
84
|
-
<button
|
|
85
|
-
type="button"
|
|
86
|
-
class="shell-home-launch"
|
|
87
|
-
onclick={() => launchApp(manifest.id)}
|
|
88
|
-
>
|
|
89
|
-
Launch
|
|
90
|
-
</button>
|
|
91
|
-
</li>
|
|
92
|
-
{/each}
|
|
93
|
-
</ul>
|
|
94
|
-
</section>
|
|
95
|
-
{/if}
|
|
96
|
-
{:else}
|
|
55
|
+
{#if elevated && adminApps.length > 0}
|
|
97
56
|
<section class="shell-home-section">
|
|
98
|
-
<h2 class="shell-home-section-title">Admin
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</button>
|
|
117
|
-
</form>
|
|
118
|
-
{#if elevateError}
|
|
119
|
-
<div class="shell-home-elevate-error">{elevateError}</div>
|
|
120
|
-
{/if}
|
|
57
|
+
<h2 class="shell-home-section-title">Admin</h2>
|
|
58
|
+
<ul class="shell-home-list">
|
|
59
|
+
{#each adminApps as manifest (manifest.id)}
|
|
60
|
+
<li class="shell-home-entry">
|
|
61
|
+
<div class="shell-home-entry-label">{manifest.label}</div>
|
|
62
|
+
<div class="shell-home-entry-meta">
|
|
63
|
+
{manifest.id} · v{manifest.version}
|
|
64
|
+
</div>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
class="shell-home-launch"
|
|
68
|
+
onclick={() => launchApp(manifest.id)}
|
|
69
|
+
>
|
|
70
|
+
Launch
|
|
71
|
+
</button>
|
|
72
|
+
</li>
|
|
73
|
+
{/each}
|
|
74
|
+
</ul>
|
|
121
75
|
</section>
|
|
122
76
|
{/if}
|
|
123
77
|
|
|
@@ -243,48 +197,8 @@
|
|
|
243
197
|
color: var(--shell-fg);
|
|
244
198
|
border-color: var(--shell-fg-subtle);
|
|
245
199
|
}
|
|
246
|
-
.shell-home-
|
|
247
|
-
margin: 0 0 12px;
|
|
200
|
+
.shell-home-user {
|
|
248
201
|
font-size: 13px;
|
|
249
|
-
color: var(--shell-fg-
|
|
250
|
-
}
|
|
251
|
-
.shell-home-elevate-form {
|
|
252
|
-
display: flex;
|
|
253
|
-
gap: 8px;
|
|
254
|
-
}
|
|
255
|
-
.shell-home-key-input {
|
|
256
|
-
flex: 1;
|
|
257
|
-
padding: 8px 12px;
|
|
258
|
-
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
259
|
-
color: var(--shell-fg);
|
|
260
|
-
border: 1px solid var(--shell-border);
|
|
261
|
-
border-radius: var(--shell-radius);
|
|
262
|
-
font-family: monospace;
|
|
263
|
-
font-size: 13px;
|
|
264
|
-
}
|
|
265
|
-
.shell-home-key-input::placeholder {
|
|
266
|
-
color: var(--shell-fg-muted);
|
|
267
|
-
}
|
|
268
|
-
.shell-home-elevate-btn {
|
|
269
|
-
padding: 8px 16px;
|
|
270
|
-
background: var(--shell-accent);
|
|
271
|
-
color: var(--shell-bg);
|
|
272
|
-
border: none;
|
|
273
|
-
border-radius: var(--shell-radius);
|
|
274
|
-
font-weight: 600;
|
|
275
|
-
cursor: pointer;
|
|
276
|
-
white-space: nowrap;
|
|
277
|
-
}
|
|
278
|
-
.shell-home-elevate-btn:disabled {
|
|
279
|
-
opacity: 0.6;
|
|
280
|
-
cursor: not-allowed;
|
|
281
|
-
}
|
|
282
|
-
.shell-home-elevate-error {
|
|
283
|
-
margin-top: 8px;
|
|
284
|
-
padding: 6px 10px;
|
|
285
|
-
font-size: 12px;
|
|
286
|
-
color: var(--shell-error, #d32f2f);
|
|
287
|
-
background: color-mix(in srgb, var(--shell-error, #d32f2f) 10%, transparent);
|
|
288
|
-
border-radius: var(--shell-radius);
|
|
202
|
+
color: var(--shell-fg-subtle);
|
|
289
203
|
}
|
|
290
204
|
</style>
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare const VERSION = "0.5.
|
|
1
|
+
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
+
export declare const VERSION = "0.5.6";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export const VERSION = '0.5.
|
|
1
|
+
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
+
export const VERSION = '0.5.6';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sh3-core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "svelte-package -i src -o dist && node --import tsx scripts/generate-api-docs.ts",
|
|
32
|
+
"build": "node --import tsx scripts/sync-version.ts && svelte-package -i src -o dist && node --import tsx scripts/generate-api-docs.ts",
|
|
33
33
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
34
34
|
"pack": "npm run build && npm pack"
|
|
35
35
|
},
|