smoodly 0.0.44 → 0.0.46
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 +3 -3
- package/dist/addresses.d.ts +6 -7
- package/dist/addresses.js +6 -4
- package/dist/admin/PublicSans-LICENSE.txt +93 -0
- package/dist/admin/auth-config.d.ts +6 -0
- package/dist/admin/auth-config.js +1 -0
- package/dist/admin/browser/fetch-op.d.ts +10 -0
- package/dist/admin/browser/fetch-op.js +44 -0
- package/dist/admin/browser/main.d.ts +1 -0
- package/dist/admin/browser/main.js +21 -0
- package/dist/admin/custom-section.d.ts +1 -1
- package/dist/admin/drift.d.ts +1 -1
- package/dist/admin/editor/EditorView.js +14 -14
- package/dist/admin/editor/autosave.js +1 -1
- package/dist/admin/editor/canvas-address.d.ts +2 -0
- package/dist/admin/editor/canvas-address.js +8 -0
- package/dist/admin/editor/overlay-geometry.d.ts +1 -1
- package/dist/admin/editor/protocol.d.ts +18 -4
- package/dist/admin/editor/protocol.js +15 -1
- package/dist/admin/editor/useCanvasBridge.d.ts +1 -1
- package/dist/admin/editor/useCanvasBridge.js +5 -6
- package/dist/admin/editor/useInlineSession.js +4 -2
- package/dist/admin/files.d.ts +11 -0
- package/dist/admin/files.generated.js +2 -0
- package/dist/admin/files.js +8 -0
- package/dist/admin/forms/useRefOptions.d.ts +2 -2
- package/dist/admin/forms/useRefOptions.js +2 -2
- package/dist/admin/forms/useRichtextEditor.js +4 -4
- package/dist/admin/handler.d.ts +40 -0
- package/dist/admin/handler.js +151 -0
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/ops-impl.d.ts +7 -1
- package/dist/admin/ops-impl.js +110 -33
- package/dist/admin/ops.d.ts +3 -2
- package/dist/admin/page-data.d.ts +21 -0
- package/dist/admin/page-data.js +4 -0
- package/dist/admin/page.d.ts +6 -0
- package/dist/admin/page.js +35 -0
- package/dist/admin/serialize.js +7 -7
- package/dist/admin/shell/AdminApp.js +2 -2
- package/dist/admin/shell/EntryForm.js +1 -1
- package/dist/admin/shell/router.d.ts +3 -16
- package/dist/admin/shell/router.js +3 -39
- package/dist/admin/shell/session.d.ts +2 -5
- package/dist/admin/supabase-auth.js +1 -1
- package/dist/admin/tree-ops.d.ts +1 -1
- package/dist/admin/ui/fonts.d.ts +12 -0
- package/dist/admin/ui/fonts.js +22 -0
- package/dist/admin/ui/theme.d.ts +13 -4
- package/dist/admin/ui/theme.js +28 -157
- package/dist/assemble.d.ts +60 -0
- package/dist/assemble.js +359 -0
- package/dist/astro/cache.d.ts +34 -0
- package/dist/astro/cache.js +69 -0
- package/dist/astro/components/Custom.astro +21 -0
- package/dist/astro/components/Guarded.astro +24 -0
- package/dist/astro/components/Head.astro +26 -0
- package/dist/astro/components/Image.astro +20 -0
- package/dist/astro/components/RichText.astro +23 -0
- package/dist/astro/components/Section.astro +20 -0
- package/dist/astro/components/Video.astro +5 -0
- package/dist/astro/components/Zone.astro +19 -0
- package/dist/astro/components.d.ts +6 -0
- package/dist/astro/components.js +7 -0
- package/dist/astro/content-loaders.d.ts +40 -0
- package/dist/astro/content-loaders.js +100 -0
- package/dist/astro/content.d.ts +9 -0
- package/dist/astro/content.js +17 -0
- package/dist/astro/custom.d.ts +1 -0
- package/dist/astro/custom.js +3 -0
- package/dist/astro/defaults.d.ts +20 -0
- package/dist/astro/defaults.js +21 -0
- package/dist/astro/env-files.d.ts +1 -0
- package/dist/astro/env-files.js +21 -0
- package/dist/astro/head.d.ts +13 -0
- package/dist/astro/head.js +19 -0
- package/dist/astro/image.d.ts +27 -0
- package/dist/astro/image.js +29 -0
- package/dist/astro/index.d.ts +43 -0
- package/dist/astro/index.js +111 -0
- package/dist/astro/locale-paths.d.ts +30 -0
- package/dist/astro/locale-paths.js +38 -0
- package/dist/astro/middleware.d.ts +1 -0
- package/dist/astro/middleware.js +83 -0
- package/dist/astro/preview-document.d.ts +1 -0
- package/dist/astro/preview-document.js +18 -0
- package/dist/astro/preview.d.ts +10 -0
- package/dist/astro/preview.js +72 -0
- package/dist/astro/richtext.d.ts +15 -0
- package/dist/astro/richtext.js +78 -0
- package/dist/astro/routes/admin.d.ts +3 -0
- package/dist/astro/routes/admin.js +41 -0
- package/dist/astro/routes/page.astro +17 -0
- package/dist/astro/routes/preview.astro +11 -0
- package/dist/astro/runtime.d.ts +32 -0
- package/dist/astro/runtime.js +46 -0
- package/dist/astro/scope.d.ts +45 -0
- package/dist/astro/scope.js +103 -0
- package/dist/astro/server.d.ts +14 -0
- package/dist/astro/server.js +45 -0
- package/dist/astro/shell.d.ts +67 -0
- package/dist/astro/shell.js +63 -0
- package/dist/astro/style.d.ts +1 -0
- package/dist/astro/style.js +11 -0
- package/dist/astro/views-check.d.ts +3 -0
- package/dist/astro/views-check.js +19 -0
- package/dist/astro/zone.d.ts +39 -0
- package/dist/astro/zone.js +33 -0
- package/dist/bind.d.ts +19 -0
- package/dist/bind.js +34 -0
- package/dist/bridge/bridge.d.ts +9 -0
- package/dist/bridge/bridge.js +107 -0
- package/dist/bridge/entry.d.ts +1 -0
- package/dist/bridge/entry.js +5 -0
- package/dist/bridge/preview-pill.d.ts +18 -0
- package/dist/bridge/preview-pill.js +115 -0
- package/dist/cli/import.d.ts +3 -0
- package/dist/cli/import.js +10 -1
- package/dist/cli/load-collections.js +4 -3
- package/dist/cli/load-config.d.ts +4 -0
- package/dist/cli/load-config.js +26 -0
- package/dist/cli/main.d.ts +7 -1
- package/dist/cli/main.js +32 -7
- package/dist/cli/migrate.d.ts +3 -0
- package/dist/cli/migrate.js +8 -1
- package/dist/cli/rebuild.d.ts +23 -0
- package/dist/cli/rebuild.js +22 -0
- package/dist/cli/space-io.d.ts +15 -1
- package/dist/cli/space-io.js +14 -0
- package/dist/client.js +5 -0
- package/dist/config.d.ts +28 -20
- package/dist/config.js +71 -75
- package/dist/content/content-api.d.ts +1 -1
- package/dist/content/node.d.ts +2 -3
- package/dist/content/node.js +2 -1
- package/dist/crash-message.d.ts +1 -0
- package/dist/crash-message.js +8 -0
- package/dist/css.d.ts +4 -0
- package/dist/css.js +5 -2
- package/dist/custom/CustomSection.d.ts +1 -1
- package/dist/custom/CustomSection.js +4 -8
- package/dist/custom/css.js +2 -2
- package/dist/custom/index.d.ts +6 -10
- package/dist/custom/index.js +1 -2
- package/dist/custom/layout.d.ts +5 -0
- package/dist/custom/layout.js +14 -0
- package/dist/entry-page.d.ts +3 -18
- package/dist/env.js +1 -1
- package/dist/fields.js +2 -1
- package/dist/global-id.js +2 -2
- package/dist/global.d.ts +5 -2
- package/dist/global.js +8 -5
- package/dist/image/next-config.d.ts +3 -0
- package/dist/image/next-config.js +12 -7
- package/dist/image/remote-pattern.d.ts +1 -0
- package/dist/image/remote-pattern.js +5 -0
- package/dist/index.d.ts +15 -17
- package/dist/index.js +10 -19
- package/dist/marks.d.ts +7 -0
- package/dist/marks.js +7 -0
- package/dist/next/admin-route.d.ts +7 -0
- package/dist/next/admin-route.js +28 -0
- package/dist/next/bridge.d.ts +1 -0
- package/dist/next/bridge.js +26 -0
- package/dist/next/canvas-boundary.d.ts +1 -1
- package/dist/next/canvas-slot.d.ts +1 -0
- package/dist/next/canvas-slot.js +4 -3
- package/dist/next/index.d.ts +2 -0
- package/dist/next/index.js +1 -0
- package/dist/next/page-renderer.d.ts +5 -1
- package/dist/next/page-renderer.js +19 -33
- package/dist/next/preview-pill-define.d.ts +1 -0
- package/dist/next/preview-pill-define.js +10 -0
- package/dist/next/preview-pill.d.ts +0 -3
- package/dist/next/preview-pill.js +7 -50
- package/dist/next/queries.d.ts +3 -28
- package/dist/next/queries.js +3 -90
- package/dist/next/reads.d.ts +2 -0
- package/dist/next/reads.js +18 -0
- package/dist/page-context.d.ts +89 -0
- package/dist/page-context.js +8 -0
- package/dist/pairing.d.ts +2 -22
- package/dist/project.d.ts +69 -0
- package/dist/project.js +210 -0
- package/dist/published.d.ts +96 -0
- package/dist/published.js +125 -0
- package/dist/react.d.ts +61 -0
- package/dist/react.js +17 -0
- package/dist/read-shapes.d.ts +1 -1
- package/dist/reads.d.ts +80 -0
- package/dist/reads.js +142 -0
- package/dist/refs.d.ts +1 -1
- package/dist/registration.d.ts +8 -0
- package/dist/registration.js +17 -0
- package/dist/render.d.ts +5 -83
- package/dist/render.js +5 -10
- package/dist/resolve.d.ts +1 -1
- package/dist/richtext/index.d.ts +6 -2
- package/dist/richtext/index.js +3 -2
- package/dist/richtext-href.d.ts +4 -0
- package/dist/richtext-href.js +17 -0
- package/dist/richtext-render.js +1 -14
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +1 -1
- package/dist/section-crash.d.ts +2 -2
- package/dist/section-crash.js +2 -7
- package/dist/section-wrapper.d.ts +3 -10
- package/dist/section-wrapper.js +3 -2
- package/dist/site.d.ts +38 -14
- package/dist/site.js +159 -286
- package/dist/smd-key.d.ts +9 -0
- package/dist/smd-key.js +1 -0
- package/dist/sql-space.d.ts +4 -0
- package/dist/sql-space.js +10 -5
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +117 -0
- package/dist/store.d.ts +1 -1
- package/dist/supabase-published-store.d.ts +35 -0
- package/dist/supabase-published-store.js +215 -0
- package/dist/supabase-store.d.ts +1 -1
- package/dist/toolset.d.ts +2 -4
- package/dist/toolset.js +6 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/view-props.d.ts +40 -0
- package/dist/view-props.js +1 -0
- package/dist/views.d.ts +36 -0
- package/dist/views.js +52 -0
- package/dist/zones.d.ts +7 -7
- package/dist/zones.js +7 -6
- package/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/package.json +46 -19
- package/dist/admin/next/bridge.d.ts +0 -2
- package/dist/admin/next/bridge.js +0 -129
- package/dist/admin/next/create-admin.d.ts +0 -19
- package/dist/admin/next/create-admin.js +0 -44
- package/dist/admin/next/index.d.ts +0 -8
- package/dist/admin/next/index.js +0 -8
- package/dist/admin/next/op-handler.d.ts +0 -18
- package/dist/admin/next/op-handler.js +0 -31
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PresenceConfig } from "../env.ts";
|
|
2
|
+
import type { AdminRegistry } from "./serialize.ts";
|
|
3
|
+
import type { AuthConfig } from "./auth-config.ts";
|
|
4
|
+
export type AdminPageData = {
|
|
5
|
+
registry: AdminRegistry;
|
|
6
|
+
/** What the browser needs to sign in: public values only. */
|
|
7
|
+
auth: AuthConfig;
|
|
8
|
+
/** What the browser needs to join the editing-presence channel; null = presence is off. */
|
|
9
|
+
presence: PresenceConfig | null;
|
|
10
|
+
/** Where operations are posted. */
|
|
11
|
+
opUrl: string;
|
|
12
|
+
/** The folder the package's files are served from, without a trailing slash. */
|
|
13
|
+
filesBase: string;
|
|
14
|
+
/** What the editor puts before a page's address to load its draft: "" where the framework serves drafts
|
|
15
|
+
* at the page's own address (Next), the preview address's base where it does not (Astro). */
|
|
16
|
+
previewBase: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const ADMIN_DATA_ID = "smoodly-admin-data";
|
|
19
|
+
export declare const ADMIN_ROOT_ID = "smoodly-admin-root";
|
|
20
|
+
/** The reserved segment under the admin path: the endpoint and the files live here. */
|
|
21
|
+
export declare const ADMIN_INTERNAL = "_smoodly";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AdminPageData } from "./page-data.ts";
|
|
2
|
+
/** The theme boot runs inside the wrapper (it reads `currentScript.parentElement`), before first paint;
|
|
3
|
+
* the script then mounts the shell into the wrapper, replacing the boot and the notice. */
|
|
4
|
+
export declare function adminPageHtml(data: AdminPageData): string;
|
|
5
|
+
/** An admin that cannot start: the message, on the admin's own background. */
|
|
6
|
+
export declare function adminErrorHtml(message: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// packages/smoodly/src/admin/page.ts
|
|
2
|
+
// The admin's one HTML page (spec 2026-10-02 astro cloud fit §4): a mount point, the page's data as JSON
|
|
3
|
+
// and the script. The same document for every address under the admin path; the shell reads its route
|
|
4
|
+
// from the URL (shell/router.ts). Framework-free: a string.
|
|
5
|
+
import { ADMIN_DATA_ID, ADMIN_ROOT_ID } from "./page-data.js";
|
|
6
|
+
import { BOOT_CSS, THEME_BOOT } from "./ui/theme.js";
|
|
7
|
+
const escapeHtml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8
|
+
/** JSON for a <script> element: every `<` escaped, so no value can close the element or open a comment. */
|
|
9
|
+
const scriptJson = (value) => JSON.stringify(value).replace(/</g, "\\u003c");
|
|
10
|
+
const notice = (text) => `<div style="height:100vh;display:grid;place-items:center;padding:0 24px;text-align:center;font:14px/1.5 system-ui,sans-serif;color:#8A8FA3">${text}</div>`;
|
|
11
|
+
const documentOf = (body) => `<!doctype html>
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="utf-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
16
|
+
<meta name="robots" content="noindex">
|
|
17
|
+
<title>Smoodly</title>
|
|
18
|
+
<style data-smoodly-boot>${BOOT_CSS}</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
${body}
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
24
|
+
`;
|
|
25
|
+
/** The theme boot runs inside the wrapper (it reads `currentScript.parentElement`), before first paint;
|
|
26
|
+
* the script then mounts the shell into the wrapper, replacing the boot and the notice. */
|
|
27
|
+
export function adminPageHtml(data) {
|
|
28
|
+
return documentOf(`<div class="sm-admin" id="${ADMIN_ROOT_ID}"><script>${THEME_BOOT}</script>${notice("Loading…")}</div>\n` +
|
|
29
|
+
`<script type="application/json" id="${ADMIN_DATA_ID}">${scriptJson(data)}</script>\n` +
|
|
30
|
+
`<script type="module" src="${escapeHtml(data.filesBase)}/admin.js"></script>`);
|
|
31
|
+
}
|
|
32
|
+
/** An admin that cannot start: the message, on the admin's own background. */
|
|
33
|
+
export function adminErrorHtml(message) {
|
|
34
|
+
return documentOf(`<div class="sm-admin">${notice(escapeHtml(message))}</div>`);
|
|
35
|
+
}
|
package/dist/admin/serialize.js
CHANGED
|
@@ -28,11 +28,11 @@ function serializeMap(map) {
|
|
|
28
28
|
}
|
|
29
29
|
/** A registered section's settings descriptors — what a visual global's form shows (spec 2026-09-14 §3). */
|
|
30
30
|
export function sectionSettings(config, name) {
|
|
31
|
-
return
|
|
31
|
+
return config.registry.sections.find((s) => s.name === name)?.settings ?? {};
|
|
32
32
|
}
|
|
33
33
|
/** Whether a section gets the custom-CSS box: the project flag, unless the schema opts out (spec 2026-09-14 §4). */
|
|
34
34
|
function sectionCss(config, name) {
|
|
35
|
-
return config.css &&
|
|
35
|
+
return config.css && config.registry.sections.find((s) => s.name === name)?.css !== false;
|
|
36
36
|
}
|
|
37
37
|
const lower = (key) => fieldLabel(key).toLowerCase();
|
|
38
38
|
function seoLabels(c) {
|
|
@@ -46,7 +46,7 @@ function seoLabels(c) {
|
|
|
46
46
|
export function serializeAdminConfig(config) {
|
|
47
47
|
const perLocale = (v) => perLocaleSegments(v, config.locales.supported);
|
|
48
48
|
return {
|
|
49
|
-
sections:
|
|
49
|
+
sections: config.registry.sections.map((schema) => {
|
|
50
50
|
const s = schema;
|
|
51
51
|
return {
|
|
52
52
|
name: s.name,
|
|
@@ -60,7 +60,7 @@ export function serializeAdminConfig(config) {
|
|
|
60
60
|
sample: s.sample,
|
|
61
61
|
};
|
|
62
62
|
}),
|
|
63
|
-
elements:
|
|
63
|
+
elements: config.registry.elements.map((schema) => {
|
|
64
64
|
const e = schema;
|
|
65
65
|
return {
|
|
66
66
|
name: e.name,
|
|
@@ -84,9 +84,9 @@ export function serializeAdminConfig(config) {
|
|
|
84
84
|
...(c.seo !== undefined ? { seo: seoLabels(c) } : {}),
|
|
85
85
|
};
|
|
86
86
|
}),
|
|
87
|
-
pages:
|
|
88
|
-
.filter((
|
|
89
|
-
.map((
|
|
87
|
+
pages: config.registry.pages
|
|
88
|
+
.filter((schema) => schema.kind === "page")
|
|
89
|
+
.map((schema) => {
|
|
90
90
|
const p = schema;
|
|
91
91
|
const slug = fixedSlug(p, config.locales.default);
|
|
92
92
|
return {
|
|
@@ -5,8 +5,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// list views in the main area, editor/entry views mounted by segment.
|
|
6
6
|
// Internal routing is the shell's own (shell/router.ts): the route is read
|
|
7
7
|
// from the URL on mount, then pushState moves between views and the shell
|
|
8
|
-
// never reloads (spec 2026-09-08 client routing). The shell
|
|
9
|
-
//
|
|
8
|
+
// never reloads (spec 2026-09-08 client routing). The shell is mounted by
|
|
9
|
+
// the prebuilt entry (browser/main.tsx) into the admin's one HTML page (page.ts).
|
|
10
10
|
//
|
|
11
11
|
// Layout is the design system's three columns: the 72px icon rail
|
|
12
12
|
// (shell/IconRail.tsx), then the main column whose views own their own
|
|
@@ -456,7 +456,7 @@ export function EntryForm({ ops, registry, collection, id }) {
|
|
|
456
456
|
return;
|
|
457
457
|
const r = await ops.entries.delete(collection, id);
|
|
458
458
|
if (r.ok)
|
|
459
|
-
void navigate(adminHref(`/${collection}`)
|
|
459
|
+
void navigate(adminHref(`/${collection}`));
|
|
460
460
|
else
|
|
461
461
|
fail(r);
|
|
462
462
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type ConfirmOptions } from "../ui/confirm.ts";
|
|
2
1
|
export type Route = {
|
|
3
2
|
pathname: string;
|
|
4
3
|
segments: string[];
|
|
@@ -19,27 +18,15 @@ export declare function isPlainLeftClick(e: {
|
|
|
19
18
|
altKey: boolean;
|
|
20
19
|
defaultPrevented: boolean;
|
|
21
20
|
}, target?: string | null): boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
/** Leaving is allowed when no guard is dirty, else when the editor
|
|
25
|
-
* confirms — asked once, however many guards are dirty. */
|
|
26
|
-
export declare function mayLeave(guards: Iterable<() => boolean>, confirm: (options: ConfirmOptions) => boolean | Promise<boolean>): Promise<boolean>;
|
|
27
|
-
/** Move to `href` in place; resolves true when the move happened. A push
|
|
28
|
-
* asks the leave guard (in the admin's dialog, so the answer is awaited);
|
|
29
|
-
* a replace never leaves the view, so it does not ask. `force` skips the
|
|
30
|
-
* guard for a view that has just saved or deleted its record — React has
|
|
31
|
-
* not flushed its setDirty(false) yet. */
|
|
21
|
+
/** Move to `href` in place; resolves true when the move happened. Every
|
|
22
|
+
* view saves by itself, so nothing asks before leaving. */
|
|
32
23
|
export declare function navigate(href: string, opts?: {
|
|
33
24
|
replace?: boolean;
|
|
34
|
-
force?: boolean;
|
|
35
25
|
}): Promise<boolean>;
|
|
36
26
|
/** The current route under `base`: empty until mount, then the URL,
|
|
37
27
|
* re-read on popstate and after navigate(). */
|
|
38
28
|
export declare function useRoute(base: string): Route;
|
|
39
|
-
/**
|
|
40
|
-
* and beforeunload covers reload, closing the tab and a real link out. */
|
|
41
|
-
export declare function useLeaveGuard(dirty: boolean): void;
|
|
42
|
-
/** The tab-close half of useLeaveGuard alone, for a view that saves on
|
|
29
|
+
/** The tab-close guard, for a view that saves on
|
|
43
30
|
* unmount (the page editor, spec 2026-09-29 pre-install fixes §4): an
|
|
44
31
|
* in-app navigation loses nothing, so only reload, closing the tab and a
|
|
45
32
|
* real link out ask, while a change or a save is pending. */
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
// framework-agnostic.
|
|
11
11
|
import { useEffect, useState } from "react";
|
|
12
12
|
import { DEFAULT_ADMIN_PATH } from "../../admin-path.js";
|
|
13
|
-
import { confirm } from "../ui/confirm.js";
|
|
14
13
|
/** The segments under `base`: "/admin/articles/x" → ["articles", "x"];
|
|
15
14
|
* the mount itself, or a pathname outside it, → []. Segments stay as
|
|
16
15
|
* written; the shell decodes the ones it uses, as it does for the
|
|
@@ -29,24 +28,7 @@ export function isPlainLeftClick(e, target) {
|
|
|
29
28
|
return false;
|
|
30
29
|
return !target || target === "_self";
|
|
31
30
|
}
|
|
32
|
-
/** The leave question, asked in the admin's own dialog (ui/confirm.ts). */
|
|
33
|
-
export const LEAVE_CONFIRM = {
|
|
34
|
-
title: "Discard unsaved changes?",
|
|
35
|
-
body: "Your latest edits have not been saved and will be lost if you leave now.",
|
|
36
|
-
confirmLabel: "Discard changes",
|
|
37
|
-
cancelLabel: "Stay",
|
|
38
|
-
destructive: true,
|
|
39
|
-
};
|
|
40
|
-
/** Leaving is allowed when no guard is dirty, else when the editor
|
|
41
|
-
* confirms — asked once, however many guards are dirty. */
|
|
42
|
-
export async function mayLeave(guards, confirm) {
|
|
43
|
-
for (const dirty of guards)
|
|
44
|
-
if (dirty())
|
|
45
|
-
return confirm(LEAVE_CONFIRM);
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
31
|
// ── the live router ──────────────────────────────────────────────────
|
|
49
|
-
const guards = new Set();
|
|
50
32
|
const listeners = new Set();
|
|
51
33
|
function currentRoute(base) {
|
|
52
34
|
const { pathname, search } = window.location;
|
|
@@ -55,14 +37,9 @@ function currentRoute(base) {
|
|
|
55
37
|
// The segments derive from the pathname and a base that never changes
|
|
56
38
|
// under a mounted shell, so two strings decide sameness.
|
|
57
39
|
const sameRoute = (a, b) => a.pathname === b.pathname && a.search === b.search;
|
|
58
|
-
/** Move to `href` in place; resolves true when the move happened.
|
|
59
|
-
*
|
|
60
|
-
* a replace never leaves the view, so it does not ask. `force` skips the
|
|
61
|
-
* guard for a view that has just saved or deleted its record — React has
|
|
62
|
-
* not flushed its setDirty(false) yet. */
|
|
40
|
+
/** Move to `href` in place; resolves true when the move happened. Every
|
|
41
|
+
* view saves by itself, so nothing asks before leaving. */
|
|
63
42
|
export async function navigate(href, opts = {}) {
|
|
64
|
-
if (!opts.replace && !opts.force && !(await mayLeave(guards, confirm)))
|
|
65
|
-
return false;
|
|
66
43
|
window.history[opts.replace ? "replaceState" : "pushState"](null, "", href);
|
|
67
44
|
for (const listener of listeners)
|
|
68
45
|
listener();
|
|
@@ -81,20 +58,7 @@ export function useRoute(base) {
|
|
|
81
58
|
}, [base]);
|
|
82
59
|
return route;
|
|
83
60
|
}
|
|
84
|
-
/**
|
|
85
|
-
* and beforeunload covers reload, closing the tab and a real link out. */
|
|
86
|
-
export function useLeaveGuard(dirty) {
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
if (!dirty)
|
|
89
|
-
return;
|
|
90
|
-
const guard = () => true;
|
|
91
|
-
guards.add(guard);
|
|
92
|
-
const onBeforeUnload = (e) => { e.preventDefault(); e.returnValue = ""; };
|
|
93
|
-
window.addEventListener("beforeunload", onBeforeUnload);
|
|
94
|
-
return () => { guards.delete(guard); window.removeEventListener("beforeunload", onBeforeUnload); };
|
|
95
|
-
}, [dirty]);
|
|
96
|
-
}
|
|
97
|
-
/** The tab-close half of useLeaveGuard alone, for a view that saves on
|
|
61
|
+
/** The tab-close guard, for a view that saves on
|
|
98
62
|
* unmount (the page editor, spec 2026-09-29 pre-install fixes §4): an
|
|
99
63
|
* in-app navigation loses nothing, so only reload, closing the tab and a
|
|
100
64
|
* real link out ask, while a change or a save is pending. */
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { type Session, type SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
key: string;
|
|
5
|
-
providers?: ("google" | "github")[];
|
|
6
|
-
};
|
|
2
|
+
import type { AuthConfig } from "../auth-config.ts";
|
|
3
|
+
export type { AuthConfig };
|
|
7
4
|
export type Arrival = "invite" | "recovery";
|
|
8
5
|
/** What an auth email link says it is: GoTrue's implicit flow puts `type=` in the fragment; a query form is accepted too. */
|
|
9
6
|
export declare function arrivalOf(hash: string, search: string): Arrival | null;
|
|
@@ -11,7 +11,7 @@ export function supabaseAdminAuth(input) {
|
|
|
11
11
|
let client = null;
|
|
12
12
|
const auth = () => {
|
|
13
13
|
if (!input.authKey) {
|
|
14
|
-
throw new Error("smoodly: set SMOODLY_PUBLISHABLE_KEY in .env.local, or in your host's environment variables — the admin signs in with it (Deploying: https://smoodly.io/docs/getting-started/deploying).");
|
|
14
|
+
throw new Error("smoodly: set SMOODLY_PUBLISHABLE_KEY in .env (Astro) or .env.local (Next.js), or in your host's environment variables — the admin signs in with it (Deploying: https://smoodly.io/docs/getting-started/deploying).");
|
|
15
15
|
}
|
|
16
16
|
return (client ?? (client = createClient(input.authUrl, input.authKey, { auth: { persistSession: false, autoRefreshToken: false } })));
|
|
17
17
|
};
|
package/dist/admin/tree-ops.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PageTree, TreeNode } from "../
|
|
1
|
+
import type { PageTree, TreeNode } from "../page-context.ts";
|
|
2
2
|
import type { ZonePolicy } from "../zones.ts";
|
|
3
3
|
import type { AdminSection, AdminGlobal } from "./serialize.ts";
|
|
4
4
|
export declare function zoneOf(tree: PageTree, id: string): string | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const FONT_FILES: readonly [{
|
|
2
|
+
readonly file: "public-sans-latin.woff2";
|
|
3
|
+
readonly source: "public-sans-latin-wght-normal.woff2";
|
|
4
|
+
readonly range: "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD";
|
|
5
|
+
}, {
|
|
6
|
+
readonly file: "public-sans-latin-ext.woff2";
|
|
7
|
+
readonly source: "public-sans-latin-ext-wght-normal.woff2";
|
|
8
|
+
readonly range: "U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF";
|
|
9
|
+
}];
|
|
10
|
+
/** The @font-face rules with the files' absolute addresses: the same text goes into the admin's own
|
|
11
|
+
* document and into the canvas frame's, where a relative address would resolve against the site page. */
|
|
12
|
+
export declare function fontFaceCss(filesBase: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// packages/smoodly/src/admin/ui/fonts.ts
|
|
2
|
+
// The admin's own font, served by the admin's route from the package's files (plan 2026-10-03 prebuilt
|
|
3
|
+
// admin): no request leaves the site for it. Public Sans, variable weight, two subsets; the browser
|
|
4
|
+
// fetches a subset only when the page shows a character in its range. `source` is the file's name in
|
|
5
|
+
// @fontsource-variable/public-sans, which the build reads (scripts/build-admin.ts).
|
|
6
|
+
export const FONT_FILES = [
|
|
7
|
+
{
|
|
8
|
+
file: "public-sans-latin.woff2",
|
|
9
|
+
source: "public-sans-latin-wght-normal.woff2",
|
|
10
|
+
range: "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
file: "public-sans-latin-ext.woff2",
|
|
14
|
+
source: "public-sans-latin-ext-wght-normal.woff2",
|
|
15
|
+
range: "U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF",
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
/** The @font-face rules with the files' absolute addresses: the same text goes into the admin's own
|
|
19
|
+
* document and into the canvas frame's, where a relative address would resolve against the site page. */
|
|
20
|
+
export function fontFaceCss(filesBase) {
|
|
21
|
+
return FONT_FILES.map((f) => `@font-face{font-family:'Public Sans';font-style:normal;font-display:swap;font-weight:100 900;src:url(${filesBase}/${f.file}) format('woff2');unicode-range:${f.range}}`).join("\n");
|
|
22
|
+
}
|
package/dist/admin/ui/theme.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/** The sheet colours, named so the page's pre-script background (BOOT_CSS) cannot drift from the tokens. */
|
|
2
|
+
export declare const SHEET_LIGHT = "#FFFFFF";
|
|
3
|
+
export declare const SHEET_DARK = "#1B1E30";
|
|
1
4
|
export declare const ADMIN_CSS: string;
|
|
2
5
|
/** Where the theme toggle's choice is remembered, browser-side only. */
|
|
3
6
|
export declare const THEME_KEY = "smoodly-theme";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
/** Applies a remembered choice before first paint, so a dark-mode editor never
|
|
8
|
+
* gets a white flash, and the inspector never jumps to a dragged width. It
|
|
9
|
+
* runs INSIDE the admin wrapper, so `currentScript.parentElement` is that
|
|
10
|
+
* element — no id lookup, and the host document's <html> is left alone. The
|
|
11
|
+
* width test repeats parseStoredWidth (ui/resize.ts) inline: an integer
|
|
12
|
+
* string at or above the minimum, else ignored. */
|
|
13
|
+
export declare const THEME_BOOT: string;
|
|
14
|
+
/** The admin page's background until the script has injected the stylesheet: the sheet colour under the
|
|
15
|
+
* tokens' own light and dark rules, so a dark-mode editor never gets a white flash (admin/page.ts). */
|
|
16
|
+
export declare const BOOT_CSS: string;
|