smoodly 0.0.45 → 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 +5 -1
- package/dist/cli/main.js +24 -6
- 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/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
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# smoodly
|
|
2
2
|
|
|
3
|
-
A code-first CMS for Next.js. Define a component once in code and it
|
|
3
|
+
A code-first CMS for Astro and Next.js. Define a component once in code and it
|
|
4
4
|
becomes a typed, editable building block your editors compose, within
|
|
5
5
|
the rails you set. Content lives in Supabase; the admin mounts into
|
|
6
|
-
your own
|
|
6
|
+
your own site at `/admin`.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
npx create-smoodly-app my-site
|
|
11
11
|
|
|
12
|
-
This creates
|
|
12
|
+
This creates an Astro or Next.js site with the admin mounted, the database schema
|
|
13
13
|
in place and a first editor who can sign in.
|
|
14
14
|
|
|
15
15
|
## Environment
|
package/dist/addresses.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LocaleSet } from "./paths.ts";
|
|
2
|
+
import { type SchemaOr } from "./registration.ts";
|
|
2
3
|
/** owner key → locale → address, without slashes at either end (`company/news`). */
|
|
3
4
|
export type Addresses = Record<string, Record<string, string>>;
|
|
4
5
|
export declare const collectionKey: (name: string) => string;
|
|
@@ -21,12 +22,10 @@ export type CodeAddress = {
|
|
|
21
22
|
collection: string | null;
|
|
22
23
|
};
|
|
23
24
|
type PageLike = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
slug?: string | Record<string, string>;
|
|
29
|
-
};
|
|
25
|
+
kind: string;
|
|
26
|
+
name: string;
|
|
27
|
+
title?: string;
|
|
28
|
+
slug?: string | Record<string, string>;
|
|
30
29
|
};
|
|
31
30
|
type CollectionLike = {
|
|
32
31
|
name: string;
|
|
@@ -35,7 +34,7 @@ type CollectionLike = {
|
|
|
35
34
|
};
|
|
36
35
|
export declare function codeAddresses(input: {
|
|
37
36
|
locales: LocaleSet;
|
|
38
|
-
pages: ReadonlyArray<PageLike
|
|
37
|
+
pages: ReadonlyArray<SchemaOr<PageLike>>;
|
|
39
38
|
collections: ReadonlyArray<CollectionLike>;
|
|
40
39
|
}): CodeAddress[];
|
|
41
40
|
export declare const addressMap: (list: CodeAddress[]) => Addresses;
|
package/dist/addresses.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// rule, so SEGMENT_RE stays the only gate. Pure: defineConfig, the
|
|
6
6
|
// materializer, the stores, the site and the admin tree all read it here.
|
|
7
7
|
import { assertSegment, fixedPageSegments } from "./paths.js";
|
|
8
|
+
import { schemaOf } from "./registration.js";
|
|
8
9
|
export const collectionKey = (name) => `collection:${name}`;
|
|
9
10
|
export const pageKey = (name) => `page:${name}`;
|
|
10
11
|
/** The collection an owner key names, or null for a fixed page. */
|
|
@@ -23,11 +24,12 @@ export function codeAddresses(input) {
|
|
|
23
24
|
const def = input.locales.default;
|
|
24
25
|
const byDefault = new Map();
|
|
25
26
|
const out = [];
|
|
26
|
-
for (const
|
|
27
|
-
|
|
27
|
+
for (const carrier of input.pages) {
|
|
28
|
+
const p = schemaOf(carrier);
|
|
29
|
+
if (p.kind !== "page" || p.slug === undefined)
|
|
28
30
|
continue;
|
|
29
|
-
const segments = fixedPageSegments(p.
|
|
30
|
-
const spec = { key: pageKey(p.
|
|
31
|
+
const segments = fixedPageSegments(p.slug, input.locales);
|
|
32
|
+
const spec = { key: pageKey(p.name), segments, title: p.title ?? p.name, template: p.name, collection: null };
|
|
31
33
|
byDefault.set(segments[def], spec);
|
|
32
34
|
out.push(spec);
|
|
33
35
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2015 The Public Sans Project Authors (https://github.com/uswds/public-sans) PublicSans-Italic[wght].ttf: Copyright 2015 The Public Sans Project Authors (https://github.com/uswds/public-sans)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdminOpAction } from "../ops.ts";
|
|
2
|
+
/** JSON has no `undefined`: in an array it becomes null, which several ops read as a value (`expected:
|
|
3
|
+
* null` is "no draft", `undefined` is "skip the check"). Trailing ones are dropped; one between two
|
|
4
|
+
* arguments cannot be sent and is named here. */
|
|
5
|
+
export declare function wireArgs(args: unknown[]): unknown[];
|
|
6
|
+
/** `fetcher` is the test seam. A network failure rejects; every answer resolves to a typed result.
|
|
7
|
+
* One operation at a time, in call order: Next queued server actions per browser, and the admin's reads
|
|
8
|
+
* and writes rely on it (a read sent after a save must see that save's token; a save typed during a
|
|
9
|
+
* publish must follow the publish). A call that fails does not hold the ones behind it. */
|
|
10
|
+
export declare function fetchOp(url: string, fetcher?: typeof fetch): AdminOpAction;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** JSON has no `undefined`: in an array it becomes null, which several ops read as a value (`expected:
|
|
2
|
+
* null` is "no draft", `undefined` is "skip the check"). Trailing ones are dropped; one between two
|
|
3
|
+
* arguments cannot be sent and is named here. */
|
|
4
|
+
export function wireArgs(args) {
|
|
5
|
+
let end = args.length;
|
|
6
|
+
while (end > 0 && args[end - 1] === undefined)
|
|
7
|
+
end--;
|
|
8
|
+
const out = args.slice(0, end);
|
|
9
|
+
if (out.some((a) => a === undefined)) {
|
|
10
|
+
throw new Error("smoodly: an operation argument is undefined between two others — pass null or a value; JSON cannot carry undefined.");
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
const noResult = (status) => ({ ok: false, code: "internal", message: `smoodly: the admin's endpoint answered ${status} without a result — see the server log.` });
|
|
15
|
+
/** `fetcher` is the test seam. A network failure rejects; every answer resolves to a typed result.
|
|
16
|
+
* One operation at a time, in call order: Next queued server actions per browser, and the admin's reads
|
|
17
|
+
* and writes rely on it (a read sent after a save must see that save's token; a save typed during a
|
|
18
|
+
* publish must follow the publish). A call that fails does not hold the ones behind it. */
|
|
19
|
+
export function fetchOp(url, fetcher = fetch) {
|
|
20
|
+
const send = async (call) => {
|
|
21
|
+
const res = await fetcher(url, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
credentials: "same-origin",
|
|
24
|
+
headers: { "content-type": "application/json", ...(call.token ? { authorization: `Bearer ${call.token}` } : {}) },
|
|
25
|
+
body: JSON.stringify({ path: call.path, args: wireArgs(call.args) }),
|
|
26
|
+
});
|
|
27
|
+
let body;
|
|
28
|
+
try {
|
|
29
|
+
body = await res.json();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return noResult(res.status);
|
|
33
|
+
}
|
|
34
|
+
if (typeof body === "object" && body !== null && typeof body.ok === "boolean")
|
|
35
|
+
return body;
|
|
36
|
+
return noResult(res.status);
|
|
37
|
+
};
|
|
38
|
+
let last = Promise.resolve();
|
|
39
|
+
return (call) => {
|
|
40
|
+
const run = last.then(() => send(call));
|
|
41
|
+
last = run.catch(() => { });
|
|
42
|
+
return run;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// packages/smoodly/src/admin/browser/main.tsx
|
|
3
|
+
// The prebuilt admin's entry (spec 2026-10-02 astro cloud fit §4): reads the page's data, writes the
|
|
4
|
+
// stylesheet into the document and mounts the shell. Bundled by scripts/build-admin.ts; no app compiles it.
|
|
5
|
+
import { createRoot } from "react-dom/client";
|
|
6
|
+
import { ADMIN_DATA_ID, ADMIN_ROOT_ID } from "../page-data.js";
|
|
7
|
+
import { setPreviewBase } from "../editor/canvas-address.js";
|
|
8
|
+
import { AdminApp } from "../shell/AdminApp.js";
|
|
9
|
+
import { fontFaceCss } from "../ui/fonts.js";
|
|
10
|
+
import { ADMIN_CSS } from "../ui/theme.js";
|
|
11
|
+
import { fetchOp } from "./fetch-op.js";
|
|
12
|
+
const data = JSON.parse(document.getElementById(ADMIN_DATA_ID).textContent ?? "");
|
|
13
|
+
setPreviewBase(data.previewBase ?? "");
|
|
14
|
+
// One <style>, fonts first: the canvas copies this element's text into the site's frame (editor/useInlineSession.ts).
|
|
15
|
+
const style = document.createElement("style");
|
|
16
|
+
style.setAttribute("data-smoodly-admin-styles", "");
|
|
17
|
+
style.textContent = `${fontFaceCss(data.filesBase)}\n${ADMIN_CSS}`;
|
|
18
|
+
document.head.appendChild(style);
|
|
19
|
+
// The page's own background rules did their job (admin/page.ts): the stylesheet owns the look now.
|
|
20
|
+
document.querySelector("style[data-smoodly-boot]")?.remove();
|
|
21
|
+
createRoot(document.getElementById(ADMIN_ROOT_ID)).render(_jsx(AdminApp, { registry: data.registry, op: fetchOp(data.opUrl), auth: data.auth, presenceConfig: data.presence ?? undefined }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdminRegistry, AdminSection } from "./serialize.ts";
|
|
2
|
-
import type { TreeNode } from "../
|
|
2
|
+
import type { TreeNode } from "../page-context.ts";
|
|
3
3
|
import type { ZonePolicy } from "../zones.ts";
|
|
4
4
|
export declare const customSection: (registry: Pick<AdminRegistry, "custom">) => AdminSection;
|
|
5
5
|
/** Only a freeform zone that says so takes a custom section (the developer's per-zone switch). */
|
package/dist/admin/drift.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdminField, AdminRegistry } from "./serialize.ts";
|
|
2
|
-
import type { PageTree } from "../
|
|
2
|
+
import type { PageTree } from "../page-context.ts";
|
|
3
3
|
export type DriftKind = "unknown-section" | "unknown-element" | "unknown-shape" | "unknown-collection" | "misfit" | "zone-disallowed" | "zone-over-max" | "orphan-field" | "orphan-zone" | "element-outside-toolset" | "localization-on" | "localization-off";
|
|
4
4
|
export type Severity = "broken" | "orphan";
|
|
5
5
|
export declare const SEVERITY: Record<DriftKind, Severity>;
|
|
@@ -38,6 +38,7 @@ import { tabOf, tabsFor } from "../forms/tabs.js";
|
|
|
38
38
|
import { InspectorFields, valueOf } from "./InspectorFields.js";
|
|
39
39
|
import { allowedSections, canInsert, fillLockedZones, findNode, insertNode, moveNode, moveNodeTo, newNodeId, nodeFromSample, patchNode, patchNodeCss, patchNodePath, patchPage, placementNode, removeNode, zoneOf, clearZone, unsetNodePath, } from "../tree-ops.js";
|
|
40
40
|
import { useDraftAutosave } from "./autosave.js";
|
|
41
|
+
import { draftAddress } from "./canvas-address.js";
|
|
41
42
|
import { CanvasOverlay } from "./CanvasOverlay.js";
|
|
42
43
|
import { InlineRichtext } from "./InlineRichtext.js";
|
|
43
44
|
import { InsertPicker } from "./InsertPicker.js";
|
|
@@ -198,7 +199,7 @@ export function EditorView({ ops, registry, id }) {
|
|
|
198
199
|
const path = paths[locale] ?? "/";
|
|
199
200
|
const label = path;
|
|
200
201
|
const siteUrl = urls[locale] ?? path;
|
|
201
|
-
const iframeSrc = ready ? withEditorParam(siteUrl) : null;
|
|
202
|
+
const iframeSrc = ready ? withEditorParam(draftAddress(siteUrl)) : null;
|
|
202
203
|
// Canvas-originated selection: the node is under the cursor already, so
|
|
203
204
|
// this path deliberately skips the scroll every other selection does.
|
|
204
205
|
// (`onCanvasSelect` is declared further down; the handler runs at event time.)
|
|
@@ -238,7 +239,7 @@ export function EditorView({ ops, registry, id }) {
|
|
|
238
239
|
setTouched(false);
|
|
239
240
|
setGlobalDirty(false);
|
|
240
241
|
setGlobalTouched(false);
|
|
241
|
-
// ops is a
|
|
242
|
+
// ops is a fetch to the admin's endpoint — it can reject outright (offline,
|
|
242
243
|
// aborted), not just resolve to a typed error. Either failure mode
|
|
243
244
|
// must land on the same full-pane error, never a hung load.
|
|
244
245
|
let previewResult;
|
|
@@ -287,10 +288,8 @@ export function EditorView({ ops, registry, id }) {
|
|
|
287
288
|
// Locked zones can't be filled by any gesture, so fill the empty
|
|
288
289
|
// ones here; a change marks the draft dirty so the debounced save
|
|
289
290
|
// persists it and the canvas shows the same tree as the outline.
|
|
290
|
-
// Read through the ref
|
|
291
|
-
//
|
|
292
|
-
// cookie write triggers one. As a dependency it would loop this
|
|
293
|
-
// effect forever (same reason AdminApp pins `ops` with useState).
|
|
291
|
+
// Read through the ref, so the registry is not a dependency of this
|
|
292
|
+
// effect: a load runs for a new page, locale or reload, nothing else.
|
|
294
293
|
const reg = registryRef.current;
|
|
295
294
|
const template = reg.pages.find((p) => p.name === result.data.record.template);
|
|
296
295
|
const draft = fillLockedZones(result.data.draft, template?.zones ?? {}, reg.sections);
|
|
@@ -435,14 +434,15 @@ export function EditorView({ ops, registry, id }) {
|
|
|
435
434
|
useSavedElsewhere(target, (event) => {
|
|
436
435
|
if (stale)
|
|
437
436
|
return;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
setReloadKey((k) => k + 1);
|
|
441
|
-
}
|
|
442
|
-
else if (editedRef.current)
|
|
437
|
+
const pending = dirty || globalDirty || saveState !== "saved" || globalEdit.saveState !== "saved";
|
|
438
|
+
if (pending && editedRef.current)
|
|
443
439
|
setStale({ by: event.email, at: Date.now() });
|
|
440
|
+
// Clean, or only the fill is pending. The fill waits in the autosave's debounce, which no flag here shows
|
|
441
|
+
// (it marks without setDirty): always reset, or it would save their reloaded tree and move the token
|
|
442
|
+
// under the editor who just saved (found 2026-10-03, on a production build fast enough to open the page
|
|
443
|
+
// before the first fill landed).
|
|
444
444
|
else
|
|
445
|
-
loadTheirsQuietly();
|
|
445
|
+
loadTheirsQuietly();
|
|
446
446
|
});
|
|
447
447
|
// Selecting from the outline must reveal the block. The canvas column is
|
|
448
448
|
// the scroller now (the iframe has no scrollbar of its own — it is sized to
|
|
@@ -661,7 +661,7 @@ export function EditorView({ ops, registry, id }) {
|
|
|
661
661
|
}
|
|
662
662
|
catch (e) {
|
|
663
663
|
console.error(e);
|
|
664
|
-
// Same transport concern as the autosave persist: the
|
|
664
|
+
// Same transport concern as the autosave persist: the fetch can reject
|
|
665
665
|
// outright instead of resolving to a typed error.
|
|
666
666
|
result = { ok: false, code: "internal", message: "Publish failed — check your connection and try again." };
|
|
667
667
|
}
|
|
@@ -898,7 +898,7 @@ export function EditorView({ ops, registry, id }) {
|
|
|
898
898
|
trailing: registry.locales.supported.length > 1 && (_jsx(LocaleSwitcher, { registry: registry, present: record ? registry.locales.supported.filter((l) => record.locales[l]) : [locale], parentLocales: parentLocales, value: locale, adding: adding, onSwitch: (l) => { void switchLocale(l); }, onAdd: (l) => { void doAddLocale(l); } })),
|
|
899
899
|
},
|
|
900
900
|
{ label },
|
|
901
|
-
] }) }), right: _jsxs(_Fragment, { children: [(touched || saveState === "error" || saveState === "refused") && _jsx(SaveIndicator, { state: saveState, pending: dirty }), ready && (_jsx(LinkButton, { icon: true, href: siteUrl, target: "_blank", rel: "noreferrer", "aria-label": "View live", title: "View live \u2014 opens the site route. While the admin's draft-mode cookie is on, this shows the DRAFT \u2014 open it in a private window to see what visitors see.", children: _jsx(Eye, { size: 16, strokeWidth: 1.5, "aria-hidden": "true" }) })), _jsx(PublishControl, { status: record?.locales[locale] ? localeStatusOf(record.locales[locale]) : null, pending: dirty || saveState !== "saved", busy: pageBusy, flash: published, onPublish: () => { void doPublish(); }, onDiscard: () => { void doDiscard(); }, onUnpublish: () => { void doUnpublish(); }, onHistory: () => { void doHistory(); } })] }) }), target && _jsx(PresenceLine, { names: editorsOf(others, target), noun: "page" }), stale && _jsx(StaleBanner, { info: stale, onReload: loadTheirs }), _jsx(DriftNotice, { draft: findings, live: liveFindings }), history && record?.locales[locale] && (_jsx(HistoryModal, { rows: history, draftId: record.locales[locale].draftVersionId, onClose: () => setHistory(null), onRestore: doRestore, render: (versionId) => _jsx("iframe", { className: "sm-history__frame", title: "Version preview", src: withVersionParam(siteUrl, versionId) }) })), _jsxs("div", { className: "sm-editor", inert: stale ? true : undefined, children: [_jsxs("div", { className: "sm-canvas", children: [(banner || diagnosis || crashedLines.length > 0) && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, padding: "0 20px 10px" }, children: [banner && (_jsxs(BannerBox, { tone: banner.fields && banner.fields.length > 0 ? "warn" : "error", onDismiss: () => setBanner(null), children: [_jsx("p", { children: banner.text }), _jsx(FieldErrorList, { fields: banner.fields ?? [] })] })), diagnosis && _jsx(CanvasWarning, { cause: diagnosis.cause, path: diagnosis.path }), crashedLines.length > 0 && (_jsx(BannerBox, { tone: "warn", children: crashedLines.map((line, i) => _jsx("p", { children: line }, i)) }))] })), _jsx("div", { className: "sm-canvas__scroll", ref: scrollerRef, onClick: (e) => { if (e.target === e.currentTarget)
|
|
901
|
+
] }) }), right: _jsxs(_Fragment, { children: [(touched || saveState === "error" || saveState === "refused") && _jsx(SaveIndicator, { state: saveState, pending: dirty }), ready && (_jsx(LinkButton, { icon: true, href: siteUrl, target: "_blank", rel: "noreferrer", "aria-label": "View live", title: "View live \u2014 opens the site route. While the admin's draft-mode cookie is on, this shows the DRAFT \u2014 open it in a private window to see what visitors see.", children: _jsx(Eye, { size: 16, strokeWidth: 1.5, "aria-hidden": "true" }) })), _jsx(PublishControl, { status: record?.locales[locale] ? localeStatusOf(record.locales[locale]) : null, pending: dirty || saveState !== "saved", busy: pageBusy, flash: published, onPublish: () => { void doPublish(); }, onDiscard: () => { void doDiscard(); }, onUnpublish: () => { void doUnpublish(); }, onHistory: () => { void doHistory(); } })] }) }), target && _jsx(PresenceLine, { names: editorsOf(others, target), noun: "page" }), stale && _jsx(StaleBanner, { info: stale, onReload: loadTheirs }), _jsx(DriftNotice, { draft: findings, live: liveFindings }), history && record?.locales[locale] && (_jsx(HistoryModal, { rows: history, draftId: record.locales[locale].draftVersionId, onClose: () => setHistory(null), onRestore: doRestore, render: (versionId) => _jsx("iframe", { className: "sm-history__frame", title: "Version preview", src: withVersionParam(draftAddress(siteUrl), versionId) }) })), _jsxs("div", { className: "sm-editor", inert: stale ? true : undefined, children: [_jsxs("div", { className: "sm-canvas", children: [(banner || diagnosis || crashedLines.length > 0) && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, padding: "0 20px 10px" }, children: [banner && (_jsxs(BannerBox, { tone: banner.fields && banner.fields.length > 0 ? "warn" : "error", onDismiss: () => setBanner(null), children: [_jsx("p", { children: banner.text }), _jsx(FieldErrorList, { fields: banner.fields ?? [] })] })), diagnosis && _jsx(CanvasWarning, { cause: diagnosis.cause, path: diagnosis.path }), crashedLines.length > 0 && (_jsx(BannerBox, { tone: "warn", children: crashedLines.map((line, i) => _jsx("p", { children: line }, i)) }))] })), _jsx("div", { className: "sm-canvas__scroll", ref: scrollerRef, onClick: (e) => { if (e.target === e.currentTarget)
|
|
902
902
|
setSelectedId(null); }, children: _jsxs("div", { className: "sm-page", style: { overflow: "hidden" }, children: [iframeSrc ? (_jsx("iframe", { ref: iframeRef, src: iframeSrc, title: "Page canvas", onLoad: onFrameLoad, style: { width: "100%", height: docH, border: 0, display: "block" } })) : (_jsx("div", { className: "sm-canvas__loading", children: "Loading canvas\u2026" })), _jsx(CanvasOverlay, { emptyZones: emptyZones, hoverRect: hoverRect, hoverGlobal: hoverGlobal, selection: selection, fieldHover: fieldHover, editing: editing, plusButtons: plusButtons, onPlus: (p, x, y) => setPicker({ zone: p.zone, index: p.index, x: x - 150, y: y + 12 }), onMove: doMove, onDuplicate: doDuplicate, onRemove: doRemove }), inline.richtext && (() => {
|
|
903
903
|
const { session } = inline.richtext;
|
|
904
904
|
const item = globalItemOf(session.node);
|
|
@@ -45,7 +45,7 @@ export function createDraftAutosave(opts) {
|
|
|
45
45
|
}
|
|
46
46
|
catch (e) {
|
|
47
47
|
console.error(e);
|
|
48
|
-
// persist is a
|
|
48
|
+
// persist is a fetch to the admin's endpoint — it can reject outright (offline,
|
|
49
49
|
// aborted), not just resolve to a typed error. Fold that into the
|
|
50
50
|
// same failure path so it isn't a stuck "saving" state.
|
|
51
51
|
result = { ok: false, code: "internal", message: "" };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Where a page's draft is loaded (DESIGN.md Vision, preview per framework): the server names a base once,
|
|
2
|
+
// in the page's data, and the canvas and the History frame put it before the page's address. Module
|
|
3
|
+
// state, set by the browser entry before the shell mounts: the admin is one script on one page.
|
|
4
|
+
let base = "";
|
|
5
|
+
export function setPreviewBase(value) {
|
|
6
|
+
base = value;
|
|
7
|
+
}
|
|
8
|
+
export const draftAddress = (siteUrl) => base + siteUrl;
|
|
@@ -50,11 +50,25 @@ export type FromBridge = {
|
|
|
50
50
|
source: "smoodly";
|
|
51
51
|
kind: "rendered";
|
|
52
52
|
};
|
|
53
|
+
/** The editor asks one thing of the canvas: render again. */
|
|
53
54
|
export type ToBridge = {
|
|
54
55
|
source: "smoodly";
|
|
55
56
|
kind: "refresh";
|
|
56
|
-
} | {
|
|
57
|
-
source: "smoodly";
|
|
58
|
-
kind: "scroll";
|
|
59
|
-
id: string;
|
|
60
57
|
};
|
|
58
|
+
/** What both guards read of a MessageEvent. */
|
|
59
|
+
type Incoming = {
|
|
60
|
+
data: unknown;
|
|
61
|
+
origin: string;
|
|
62
|
+
source: unknown;
|
|
63
|
+
};
|
|
64
|
+
/** The bridge's guard: a message counts only when the window framing this one sent it, from this
|
|
65
|
+
* origin, in Smoodly's envelope. The canvas is same-origin with the admin by design (DESIGN.md §2). */
|
|
66
|
+
export declare function toBridgeMessage(e: Incoming, win: {
|
|
67
|
+
parent: unknown;
|
|
68
|
+
location: {
|
|
69
|
+
origin: string;
|
|
70
|
+
};
|
|
71
|
+
}): ToBridge | null;
|
|
72
|
+
/** The editor's guard: a message counts only when the canvas frame sent it, from the admin's own origin. */
|
|
73
|
+
export declare function fromBridgeMessage(e: Incoming, frame: unknown, origin: string): FromBridge | null;
|
|
74
|
+
export {};
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/** The bridge's guard: a message counts only when the window framing this one sent it, from this
|
|
2
|
+
* origin, in Smoodly's envelope. The canvas is same-origin with the admin by design (DESIGN.md §2). */
|
|
3
|
+
export function toBridgeMessage(e, win) {
|
|
4
|
+
if (e.source !== win.parent || e.origin !== win.location.origin)
|
|
5
|
+
return null;
|
|
6
|
+
const m = e.data;
|
|
7
|
+
return typeof m === "object" && m?.source === "smoodly" && m.kind === "refresh" ? { source: "smoodly", kind: "refresh" } : null;
|
|
8
|
+
}
|
|
9
|
+
/** The editor's guard: a message counts only when the canvas frame sent it, from the admin's own origin. */
|
|
10
|
+
export function fromBridgeMessage(e, frame, origin) {
|
|
11
|
+
if (!frame || e.source !== frame || e.origin !== origin)
|
|
12
|
+
return null;
|
|
13
|
+
const m = e.data;
|
|
14
|
+
return typeof m === "object" && m?.source === "smoodly" ? m : null;
|
|
15
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RefObject } from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type BridgeRect, type FieldRect, type ToBridge, type ZoneRect } from "./protocol.ts";
|
|
3
3
|
import type { CrashedSection } from "../../marks.ts";
|
|
4
4
|
export type Geometry = "waiting" | "ok" | "missing";
|
|
5
5
|
export declare function useCanvasBridge({ iframeRef, iframeSrc, onSelect, onHover, onRendered }: {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// The admin side of the editor⇄canvas protocol: the geometry the bridge
|
|
2
2
|
// reports (node rects, empty zones, document height), select/hover
|
|
3
|
-
// forwarded from the iframe, and `postToCanvas` for refresh
|
|
3
|
+
// forwarded from the iframe, and `postToCanvas` for refresh.
|
|
4
4
|
// Silence is survivable — GEOMETRY_TIMEOUT_MS after the frame's load
|
|
5
5
|
// with no bridge message, `geometry` turns "missing" and the page editor
|
|
6
6
|
// diagnoses why (spec 2026-09-27 shadowed-page detector §2); the iframe
|
|
7
7
|
// and the outline keep working.
|
|
8
8
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
import { fromBridgeMessage } from "./protocol.js";
|
|
9
10
|
const GEOMETRY_TIMEOUT_MS = 4000;
|
|
10
11
|
export function useCanvasBridge({ iframeRef, iframeSrc, onSelect, onHover, onRendered }) {
|
|
11
12
|
const [rects, setRects] = useState([]);
|
|
@@ -40,14 +41,12 @@ export function useCanvasBridge({ iframeRef, iframeSrc, onSelect, onHover, onRen
|
|
|
40
41
|
const postToCanvas = useCallback((m) => {
|
|
41
42
|
if (m.kind === "refresh" && !heard.current)
|
|
42
43
|
missedRefresh.current = true;
|
|
43
|
-
iframeRef.current?.contentWindow?.postMessage(m,
|
|
44
|
+
iframeRef.current?.contentWindow?.postMessage(m, window.location.origin);
|
|
44
45
|
}, [iframeRef]);
|
|
45
46
|
useEffect(() => {
|
|
46
47
|
const onMessage = (e) => {
|
|
47
|
-
const m = e.
|
|
48
|
-
if (m
|
|
49
|
-
return;
|
|
50
|
-
if (e.source !== iframeRef.current?.contentWindow)
|
|
48
|
+
const m = fromBridgeMessage(e, iframeRef.current?.contentWindow, window.location.origin);
|
|
49
|
+
if (!m)
|
|
51
50
|
return;
|
|
52
51
|
setGeometry("ok"); // any bridge message: Smoodly rendered the canvas with its bridge
|
|
53
52
|
if (!heard.current) {
|
|
@@ -216,13 +216,15 @@ export function useInlineSession({ iframeRef, postToCanvas, patch }) {
|
|
|
216
216
|
clearTimeout(closeTimer.current); }, []);
|
|
217
217
|
return { session: state.session, richtext, start, end, writeText, markChanged, onSaved, onRendered, onSelection, onCanvasChange };
|
|
218
218
|
}
|
|
219
|
-
/** The admin stylesheet inside the iframe, once: the cards inside the canvas editor are admin UI.
|
|
219
|
+
/** The admin stylesheet inside the iframe, once: the cards inside the canvas editor are admin UI. The text is
|
|
220
|
+
* the admin document's own, fonts included (the entry writes it with the files' absolute addresses,
|
|
221
|
+
* browser/main.tsx); the bare rules when no page mounted one (a test). */
|
|
220
222
|
function ensureStyles(d) {
|
|
221
223
|
if (d.querySelector("style[data-smoodly-admin-styles]"))
|
|
222
224
|
return;
|
|
223
225
|
const style = d.createElement("style");
|
|
224
226
|
style.setAttribute("data-smoodly-admin-styles", "");
|
|
225
|
-
style.textContent = ADMIN_CSS;
|
|
227
|
+
style.textContent = document.querySelector("style[data-smoodly-admin-styles]")?.textContent ?? ADMIN_CSS;
|
|
226
228
|
d.head.appendChild(style);
|
|
227
229
|
}
|
|
228
230
|
/** The caret at a point, for the text session (spec §4). */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type AdminFile = {
|
|
2
|
+
type: string;
|
|
3
|
+
body: string;
|
|
4
|
+
encoding?: "base64";
|
|
5
|
+
};
|
|
6
|
+
/** `hash` names the build: the files are served under it and cached for good. */
|
|
7
|
+
export type AdminFiles = {
|
|
8
|
+
hash: string;
|
|
9
|
+
files: Record<string, AdminFile>;
|
|
10
|
+
};
|
|
11
|
+
export declare function loadAdminFiles(): Promise<AdminFiles>;
|