smoodly 0.0.45 → 0.0.47
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/health.d.ts +4 -0
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/ops-impl.d.ts +7 -1
- package/dist/admin/ops-impl.js +112 -35
- 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/ContentHealthPage.js +3 -1
- 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 +13 -0
- package/dist/cli/migrate.js +30 -2
- 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/document-stamp.d.ts +7 -0
- package/dist/document-stamp.js +48 -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 +85 -0
- package/dist/project.js +228 -0
- package/dist/published.d.ts +115 -0
- package/dist/published.js +136 -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 +40 -14
- package/dist/site.js +199 -284
- package/dist/smd-key.d.ts +9 -0
- package/dist/smd-key.js +1 -0
- package/dist/sql-space.d.ts +5 -0
- package/dist/sql-space.js +11 -5
- package/dist/sql.d.ts +2 -0
- package/dist/sql.js +127 -1
- package/dist/store.d.ts +1 -1
- package/dist/supabase-published-store.d.ts +45 -0
- package/dist/supabase-published-store.js +247 -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/migrations/20261008120000_smoodly_v10.sql +47 -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
|
@@ -11,17 +11,22 @@ export function isPrivateHost(hostname) {
|
|
|
11
11
|
const [a, b] = [Number(m[1]), Number(m[2])];
|
|
12
12
|
return a === 127 || a === 10 || (a === 192 && b === 168) || (a === 172 && b >= 16 && b <= 31);
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return { remotePatterns: [] };
|
|
18
|
-
}
|
|
14
|
+
/** The Supabase host's public media path as a remote pattern. Defined here, not in remote-pattern.ts: this
|
|
15
|
+
* entry loads in plain Node and imports nothing; remote-pattern.ts re-exports it for the Astro side. */
|
|
16
|
+
export function mediaRemotePattern(url) {
|
|
19
17
|
const u = new URL(url);
|
|
20
|
-
|
|
18
|
+
return {
|
|
21
19
|
protocol: u.protocol === "http:" ? "http" : "https",
|
|
22
20
|
hostname: u.hostname,
|
|
23
21
|
...(u.port ? { port: u.port } : {}),
|
|
24
22
|
pathname: "/storage/v1/object/public/media/**",
|
|
25
23
|
};
|
|
26
|
-
|
|
24
|
+
}
|
|
25
|
+
export function smoodlyImages(url = process.env.SMOODLY_URL) {
|
|
26
|
+
if (!url) {
|
|
27
|
+
console.warn("smoodly: SMOODLY_URL is not set — next/image will refuse Supabase media URLs until it is.");
|
|
28
|
+
return { remotePatterns: [] };
|
|
29
|
+
}
|
|
30
|
+
const pattern = mediaRemotePattern(url);
|
|
31
|
+
return { remotePatterns: [pattern], ...(isPrivateHost(new URL(url).hostname) ? { dangerouslyAllowLocalIP: true } : {}) };
|
|
27
32
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { mediaRemotePattern, type RemotePattern } from "./next-config.ts";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The Supabase host's public media path as a remote pattern (spec 2026-09-08 media §9): what next/image
|
|
2
|
+
// (next-config.ts) and Astro's image service (astro/index.ts) are both told they may resize from.
|
|
3
|
+
// The function lives in next-config.ts, which must import nothing (it loads in plain Node from
|
|
4
|
+
// next.config.ts); this module is the import path the Astro side uses.
|
|
5
|
+
export { mediaRemotePattern } from "./next-config.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { section, element } from "./pairing.tsx";
|
|
2
1
|
import { collection } from "./collections.ts";
|
|
3
2
|
import { toolset } from "./toolset.ts";
|
|
4
|
-
import { Section } from "./section-wrapper.tsx";
|
|
5
|
-
import { page } from "./render.tsx";
|
|
6
|
-
import { entryPage } from "./entry-page.tsx";
|
|
7
3
|
import { globalItem } from "./global.ts";
|
|
8
4
|
import { marks } from "./marks.ts";
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
5
|
+
export { createSupabaseStores, siteOptions } from "./bind.ts";
|
|
6
|
+
export { schemaOf } from "./registration.ts";
|
|
7
|
+
export type { SchemaOr } from "./registration.ts";
|
|
8
|
+
export { EMPTY_PAGE_CONTEXT } from "./page-context.ts";
|
|
9
|
+
export type { PageTree, TreeNode, ZoneContent, EditorWrap, PageContext, PageContextLink, PageContextAlternate, PageLink } from "./page-context.ts";
|
|
13
10
|
export type { PageSchema } from "./page.ts";
|
|
14
11
|
export { fixedSlug } from "./page.ts";
|
|
15
|
-
|
|
12
|
+
/** The one rule for a link an editor typed: http(s), mailto, tel and site-relative targets pass; anything else is null, so a view renders no href (spec 2026-09-07 list field §5). React neutralises javascript: URLs; Astro does not, so an .astro view uses this. */
|
|
13
|
+
export { safeHref } from "./richtext-href.ts";
|
|
14
|
+
export type { EntryPageSchema, EntryPageProps, EntryFields } from "./view-props.ts";
|
|
16
15
|
export { f, normalizeOptions, optionsOf, optionValues } from "./fields.ts";
|
|
17
16
|
export { when, evaluate, isHidden, assertConditions, splitConditionPath } from "./conditions.ts";
|
|
18
17
|
export { DEFAULT_STYLE_VALUES, STYLE_KEYS, SPACING_NONE, resolveStyleValues, styleDescriptors, inlineStyles } from "./styles.ts";
|
|
@@ -53,6 +52,9 @@ export { SupabaseEntryStore } from "./supabase-entry-store.ts";
|
|
|
53
52
|
export { SupabasePathIndex } from "./supabase-path-index.ts";
|
|
54
53
|
export type { EntryStore, EntryRecord, EntryLocale, EntryVersion, EntryScan, EntryUsage, ListOptions, CreateEntryOptions, AddEntryLocaleOptions, EntryStoreOptions, Where, WhereValue, SnapshotQuery, SnapshotRow, } from "./entry-store.ts";
|
|
55
54
|
export { entryErrors, compareEntries, DEFAULT_ENTRY_LOCALES, localizedKeys, splitFields, entryLocaleRow, entryFieldsIn, entryRefEdges, entryTitleIn, matchesWhere, } from "./entry-store.ts";
|
|
55
|
+
export { MemoryPublishedStore } from "./published.ts";
|
|
56
|
+
export { SupabasePublishedStore } from "./supabase-published-store.ts";
|
|
57
|
+
export type { PublishedStore, PublishedPageRow, PublishedEntryRow, PublishedRoute, PublishedTreeRow, PublishedQuery, PublishedKey } from "./published.ts";
|
|
56
58
|
export { affectedTargets, pageTag, entryTag, globalTag, pagesTreeTag, collectionTag } from "./revalidate.ts";
|
|
57
59
|
export type { AffectedTargets } from "./revalidate.ts";
|
|
58
60
|
export { resolveTree, resolveFields, resolveMedia } from "./resolve.ts";
|
|
@@ -71,18 +73,19 @@ export { node } from "./content/node.ts";
|
|
|
71
73
|
export type { CollectionSeo } from "./seo.ts";
|
|
72
74
|
export type { EntryRef, EntryItem, PageItem, GlobalItem, VisualGlobalItem, Seo } from "./read-shapes.ts";
|
|
73
75
|
export type { RefTo } from "./fields.ts";
|
|
74
|
-
export type { Props
|
|
76
|
+
export type { Props } from "./view-props.ts";
|
|
75
77
|
export type { CollectionSchema, EntryOrder } from "./collections.ts";
|
|
76
78
|
export { DEFAULT_TOOLSET, type Toolset, type ToolsetMark, type ToolsetNode } from "./toolset.ts";
|
|
77
79
|
export { collectionOrder, collectionDepth, DEFAULT_ORDER } from "./collections.ts";
|
|
78
80
|
export type { ZonePolicy } from "./zones.ts";
|
|
79
81
|
export { GLOBAL_NODE_TYPE, SETTINGS_KEY, STYLES_KEY, CSS_KEY, globalLink } from "./global.ts";
|
|
80
|
-
export { SMD_KEY } from "./
|
|
81
|
-
export type { SmdInternal } from "./
|
|
82
|
+
export { SMD_KEY } from "./smd-key.ts";
|
|
83
|
+
export type { SmdInternal } from "./smd-key.ts";
|
|
82
84
|
export { CSS_MAX, cssProblem, safeCss, scopedCss } from "./css.ts";
|
|
83
85
|
export { globalEntryId } from "./global-id.ts";
|
|
84
86
|
export { DEFAULT_SPACE_ID } from "./sql-space.ts";
|
|
85
87
|
export type { GlobalSchema, GlobalLink } from "./global.ts";
|
|
88
|
+
/** The registration namespace: schemas and their parts. The React pairing (`smoodly.section`, `<smoodly.Section>`) is smoodly/react's. */
|
|
86
89
|
export declare const smoodly: {
|
|
87
90
|
schema: {
|
|
88
91
|
page: typeof import("./page.ts").pageSchema;
|
|
@@ -117,14 +120,9 @@ export declare const smoodly: {
|
|
|
117
120
|
contexts: ("richtext" | "column")[];
|
|
118
121
|
}): import("./schema.ts").ElementSchema<F, S>;
|
|
119
122
|
};
|
|
120
|
-
section: typeof section;
|
|
121
|
-
element: typeof element;
|
|
122
|
-
page: typeof page;
|
|
123
|
-
entryPage: typeof entryPage;
|
|
124
123
|
collection: typeof collection;
|
|
125
124
|
toolset: typeof toolset;
|
|
126
125
|
global: typeof globalItem;
|
|
127
|
-
Section: typeof Section;
|
|
128
126
|
marks: typeof marks;
|
|
129
127
|
};
|
|
130
128
|
export { NODE_ATTR, ZONE_ATTR, EMPTY_ATTR, BLANK_ATTR, FIELD_ATTR, ITEM_ATTR, EDITOR_ATTR, resolveMarkPath } from "./marks.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { schema } from "./schema.js";
|
|
2
|
-
import { section, element } from "./pairing.js";
|
|
3
2
|
import { collection } from "./collections.js";
|
|
4
3
|
import { toolset } from "./toolset.js";
|
|
5
|
-
import { Section } from "./section-wrapper.js";
|
|
6
|
-
import { page } from "./render.js";
|
|
7
|
-
import { entryPage } from "./entry-page.js";
|
|
8
4
|
import { globalItem } from "./global.js";
|
|
9
5
|
import { marks } from "./marks.js";
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
6
|
+
export { createSupabaseStores, siteOptions } from "./bind.js";
|
|
7
|
+
export { schemaOf } from "./registration.js";
|
|
8
|
+
export { EMPTY_PAGE_CONTEXT } from "./page-context.js";
|
|
12
9
|
export { fixedSlug } from "./page.js";
|
|
10
|
+
/** The one rule for a link an editor typed: http(s), mailto, tel and site-relative targets pass; anything else is null, so a view renders no href (spec 2026-09-07 list field §5). React neutralises javascript: URLs; Astro does not, so an .astro view uses this. */
|
|
11
|
+
export { safeHref } from "./richtext-href.js";
|
|
13
12
|
export { f, normalizeOptions, optionsOf, optionValues } from "./fields.js";
|
|
14
13
|
export { when, evaluate, isHidden, assertConditions, splitConditionPath } from "./conditions.js";
|
|
15
14
|
export { DEFAULT_STYLE_VALUES, STYLE_KEYS, SPACING_NONE, resolveStyleValues, styleDescriptors, inlineStyles } from "./styles.js";
|
|
@@ -39,6 +38,8 @@ export { MemoryEntryStore } from "./entry-store.js";
|
|
|
39
38
|
export { SupabaseEntryStore } from "./supabase-entry-store.js";
|
|
40
39
|
export { SupabasePathIndex } from "./supabase-path-index.js";
|
|
41
40
|
export { entryErrors, compareEntries, DEFAULT_ENTRY_LOCALES, localizedKeys, splitFields, entryLocaleRow, entryFieldsIn, entryRefEdges, entryTitleIn, matchesWhere, } from "./entry-store.js";
|
|
41
|
+
export { MemoryPublishedStore } from "./published.js";
|
|
42
|
+
export { SupabasePublishedStore } from "./supabase-published-store.js";
|
|
42
43
|
export { affectedTargets, pageTag, entryTag, globalTag, pagesTreeTag, collectionTag } from "./revalidate.js";
|
|
43
44
|
export { resolveTree, resolveFields, resolveMedia } from "./resolve.js";
|
|
44
45
|
export { SEGMENT_RE, isSegment, assertSegment, slugify, joinPath, splitPath, segmentFor, isHomeNode, pagePathIn, chainOf, assertDepth, pagePath, pagePathRows, entryPathRows, collectionSegment, buildPageTree, perLocaleSegments, assertSupportedLocale, unsupportedLocale, pageAlternates, entryAlternates, } from "./paths.js";
|
|
@@ -50,22 +51,12 @@ export { node } from "./content/node.js";
|
|
|
50
51
|
export { DEFAULT_TOOLSET } from "./toolset.js";
|
|
51
52
|
export { collectionOrder, collectionDepth, DEFAULT_ORDER } from "./collections.js";
|
|
52
53
|
export { GLOBAL_NODE_TYPE, SETTINGS_KEY, STYLES_KEY, CSS_KEY, globalLink } from "./global.js";
|
|
53
|
-
export { SMD_KEY } from "./
|
|
54
|
+
export { SMD_KEY } from "./smd-key.js";
|
|
54
55
|
export { CSS_MAX, cssProblem, safeCss, scopedCss } from "./css.js";
|
|
55
56
|
export { globalEntryId } from "./global-id.js";
|
|
56
57
|
export { DEFAULT_SPACE_ID } from "./sql-space.js";
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
section,
|
|
60
|
-
element,
|
|
61
|
-
page,
|
|
62
|
-
entryPage,
|
|
63
|
-
collection,
|
|
64
|
-
toolset,
|
|
65
|
-
global: globalItem,
|
|
66
|
-
Section,
|
|
67
|
-
marks,
|
|
68
|
-
};
|
|
58
|
+
/** The registration namespace: schemas and their parts. The React pairing (`smoodly.section`, `<smoodly.Section>`) is smoodly/react's. */
|
|
59
|
+
export const smoodly = { schema, collection, toolset, global: globalItem, marks };
|
|
69
60
|
export { NODE_ATTR, ZONE_ATTR, EMPTY_ATTR, BLANK_ATTR, FIELD_ATTR, ITEM_ATTR, EDITOR_ATTR, resolveMarkPath } from "./marks.js";
|
|
70
61
|
export { resolveSmoodlyEnv, smoodlyEnv, presenceConfig, siteUrl } from "./env.js";
|
|
71
62
|
export { clientOptions, createSmoodlyClient } from "./client.js";
|
package/dist/marks.d.ts
CHANGED
|
@@ -7,6 +7,13 @@ export declare const EMPTY_ATTR = "data-smd-empty";
|
|
|
7
7
|
/** A section that renders no text and no media, stamped by the bridge in
|
|
8
8
|
* editor mode so the canvas can give it a height to see and select. */
|
|
9
9
|
export declare const BLANK_ATTR = "data-smd-blank";
|
|
10
|
+
/** The one rule the canvas needs from the page itself; the server renders it in editor mode, in a <style>
|
|
11
|
+
* the canvas diagnosis looks for, so it lives here and a server imports nothing of the bridge. The
|
|
12
|
+
* editor sizes the frame to the reported height, so the document's own viewport must never scroll
|
|
13
|
+
* (2026-09-26): a fractional content height (4595.3px in a 4595px frame) left it scrollable by a hair,
|
|
14
|
+
* and a trackpad scroll over the canvas showed the frame's overlay scrollbar. Hidden overflow drops no
|
|
15
|
+
* layout, and the live site never carries this. */
|
|
16
|
+
export declare const BRIDGE_CSS = "html{overflow:hidden}[data-smd-node][data-smd-blank]{min-height:7rem}";
|
|
10
17
|
/** The element that renders a field's value verbatim. */
|
|
11
18
|
export declare const FIELD_ATTR = "data-smd-field";
|
|
12
19
|
/** A list item's or object field's root: scopes the field names inside it. */
|
package/dist/marks.js
CHANGED
|
@@ -6,6 +6,13 @@ export const EMPTY_ATTR = "data-smd-empty";
|
|
|
6
6
|
/** A section that renders no text and no media, stamped by the bridge in
|
|
7
7
|
* editor mode so the canvas can give it a height to see and select. */
|
|
8
8
|
export const BLANK_ATTR = "data-smd-blank";
|
|
9
|
+
/** The one rule the canvas needs from the page itself; the server renders it in editor mode, in a <style>
|
|
10
|
+
* the canvas diagnosis looks for, so it lives here and a server imports nothing of the bridge. The
|
|
11
|
+
* editor sizes the frame to the reported height, so the document's own viewport must never scroll
|
|
12
|
+
* (2026-09-26): a fractional content height (4595.3px in a 4595px frame) left it scrollable by a hair,
|
|
13
|
+
* and a trackpad scroll over the canvas showed the frame's overlay scrollbar. Hidden overflow drops no
|
|
14
|
+
* layout, and the live site never carries this. */
|
|
15
|
+
export const BRIDGE_CSS = `html{overflow:hidden}[${NODE_ATTR}][${BLANK_ATTR}]{min-height:7rem}`;
|
|
9
16
|
/** The element that renders a field's value verbatim. */
|
|
10
17
|
export const FIELD_ATTR = "data-smd-field";
|
|
11
18
|
/** A list item's or object field's root: scopes the field names inside it. */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AdminHandlerDeps } from "../admin/handler.ts";
|
|
2
|
+
export type AdminRouteDeps = Omit<AdminHandlerDeps, "effects">;
|
|
3
|
+
/** `deps` is read per request: the app's glue reads the environment there, so `next build` needs no keys. */
|
|
4
|
+
export declare function createAdminRoute(deps: () => AdminRouteDeps): {
|
|
5
|
+
GET: (request: Request) => Promise<Response>;
|
|
6
|
+
POST: (request: Request) => Promise<Response>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// packages/smoodly/src/next/admin-route.ts
|
|
2
|
+
// The admin in a Next app (spec 2026-10-02 astro cloud fit §4): one route handler file exports GET and
|
|
3
|
+
// POST from here, and that is the whole mount — the page, the package's files and the operations
|
|
4
|
+
// endpoint (admin/handler.ts). This file adds only what is Next's: the cache and draft mode.
|
|
5
|
+
import { revalidateTag } from "next/cache";
|
|
6
|
+
import { draftMode } from "next/headers";
|
|
7
|
+
import { connection } from "next/server";
|
|
8
|
+
import { createAdminHandler } from "../admin/handler.js";
|
|
9
|
+
const effects = {
|
|
10
|
+
// `{ expire: 0 }`, not a profile: a profile is stale-while-revalidate, so the first request after a
|
|
11
|
+
// publish would still get the cached page (or the cached 404). updateTag is for server actions only.
|
|
12
|
+
revalidate: (tag) => revalidateTag(tag, { expire: 0 }),
|
|
13
|
+
draft: {
|
|
14
|
+
enable: async () => (await draftMode()).enable(),
|
|
15
|
+
disable: async () => (await draftMode()).disable(),
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
/** `deps` is read per request: the app's glue reads the environment there, so `next build` needs no keys. */
|
|
19
|
+
export function createAdminRoute(deps) {
|
|
20
|
+
const handle = createAdminHandler(() => ({ ...deps(), effects }));
|
|
21
|
+
// A request first: the handler catches every error to answer with a named one, and Next's own
|
|
22
|
+
// "this route is dynamic" signal must not be among them.
|
|
23
|
+
const route = async (request) => {
|
|
24
|
+
await connection();
|
|
25
|
+
return handle(request);
|
|
26
|
+
};
|
|
27
|
+
return { GET: route, POST: route };
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function EditorBridge(): null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// The plain bridge in a Next app (spec 2026-10-02 astro cloud fit §5): started once inside the canvas
|
|
3
|
+
// frame with the router's refresh, so a save re-renders the route in place — no reload, the inline
|
|
4
|
+
// editing session and the scroll position survive. Loaded lazily through the canvas stub in editor
|
|
5
|
+
// mode only (canvas-slot.tsx); a live page never downloads it.
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
import { useRouter } from "next/navigation";
|
|
8
|
+
import { startBridge } from "../bridge/bridge.js";
|
|
9
|
+
export function EditorBridge() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
const bridge = useRef(null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const started = startBridge({ refresh: () => router.refresh() });
|
|
14
|
+
bridge.current = started;
|
|
15
|
+
return () => { started.stop(); bridge.current = null; };
|
|
16
|
+
}, [router]);
|
|
17
|
+
// Every later commit of this component — each refresh re-renders the route — tells the editor the new
|
|
18
|
+
// markup is in the DOM. The mount's own report is startBridge's.
|
|
19
|
+
const mounted = useRef(false);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (mounted.current)
|
|
22
|
+
bridge.current?.rendered();
|
|
23
|
+
mounted.current = true;
|
|
24
|
+
});
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -11,6 +11,6 @@ type State = {
|
|
|
11
11
|
export declare class CanvasBoundary extends Component<Props, State> {
|
|
12
12
|
state: State;
|
|
13
13
|
static getDerivedStateFromError(error: unknown): State;
|
|
14
|
-
render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> |
|
|
14
|
+
render(): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
|
|
15
15
|
}
|
|
16
16
|
export {};
|
package/dist/next/canvas-slot.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// The one Smoodly client module a live page carries (spec 2026-09-29
|
|
3
3
|
// pre-install fixes §1). Next ships every client module a route's server
|
|
4
4
|
// graph imports, rendered or not; this stub is that module, and the bridge
|
|
5
|
-
// and the canvas net load from it lazily
|
|
6
|
-
//
|
|
5
|
+
// and the canvas net load from it lazily in editor mode, and the preview
|
|
6
|
+
// pill's definition in draft mode, so a live page downloads none of them.
|
|
7
7
|
import dynamic from "next/dynamic";
|
|
8
|
-
export const CanvasBridgeSlot = dynamic(() => import("
|
|
8
|
+
export const CanvasBridgeSlot = dynamic(() => import("./bridge.js").then((m) => m.EditorBridge));
|
|
9
9
|
export const CanvasNetSlot = dynamic(() => import("./canvas-boundary.js").then((m) => m.CanvasBoundary));
|
|
10
|
+
export const PreviewPillSlot = dynamic(() => import("./preview-pill-define.js").then((m) => m.PreviewPillDefine));
|
package/dist/next/index.d.ts
CHANGED
|
@@ -5,3 +5,5 @@ export { createSmoodlyQueries } from "./queries.ts";
|
|
|
5
5
|
export type { SmoodlyQueries, QueryOptions, EntriesQuery, TypedEntryItem, GlobalValue } from "./queries.ts";
|
|
6
6
|
export { previewRoute } from "./preview-route.ts";
|
|
7
7
|
export { previewPill } from "./preview-pill.tsx";
|
|
8
|
+
export { createAdminRoute } from "./admin-route.ts";
|
|
9
|
+
export type { AdminRouteDeps } from "./admin-route.ts";
|
package/dist/next/index.js
CHANGED
|
@@ -13,7 +13,7 @@ type SectionEntry = ComponentType<any> & {
|
|
|
13
13
|
fields: Record<string, Descriptor>;
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
|
-
/** A page
|
|
16
|
+
/** A page type's or an entry page's component — the `pages` of defineViews. */
|
|
17
17
|
export type PageRegistration = ((props: any) => ReactElement) & {
|
|
18
18
|
schema: {
|
|
19
19
|
kind: string;
|
|
@@ -26,6 +26,10 @@ export type PageRendererOptions = {
|
|
|
26
26
|
sections: SectionEntry[];
|
|
27
27
|
/** Registered Elements: what the custom section's default richtext renderer needs when no RichText is bound (spec 2026-09-14 §6). */
|
|
28
28
|
elements?: SectionEntry[];
|
|
29
|
+
/** The project's bound RichText for the custom section's columns; replaces `config.custom.RichText` (plan 2026-10-02 registration split). */
|
|
30
|
+
RichText?: ComponentType<{
|
|
31
|
+
doc: unknown;
|
|
32
|
+
}>;
|
|
29
33
|
pages: ReadonlyArray<PageRegistration>;
|
|
30
34
|
/** Locale-relative CMS path → site URL. Default: the path itself. With
|
|
31
35
|
* next-intl: `(path, locale) => getPathname({ locale, href: path })`. */
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// The site-route seam (spec 2026-09-05 §5): the two route files call
|
|
3
|
-
// renderSmoodlyPath; everything else — request gating, draft mode,
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// renderSmoodlyPath; everything else — request gating, draft mode, 404s,
|
|
4
|
+
// the editor bridge (through the lazy canvas stub) — lives here, over the
|
|
5
|
+
// framework-free site layer. The reads are the read layer's units, keyed
|
|
6
|
+
// by (locale, path, target) and tagged by id (../reads.ts), run by Next's
|
|
7
|
+
// adapter (./reads.ts). Bound once by the app, exactly like createAdminRoute.
|
|
8
8
|
import { Suspense } from "react";
|
|
9
|
-
import { unstable_cache } from "next/cache";
|
|
10
9
|
import { draftMode } from "next/headers";
|
|
11
10
|
import { notFound, permanentRedirect, unstable_rethrow } from "next/navigation";
|
|
12
11
|
import { connection } from "next/server";
|
|
12
|
+
import { BRIDGE_CSS } from "../marks.js";
|
|
13
13
|
import { CanvasBridgeSlot, CanvasNetSlot } from "./canvas-slot.js";
|
|
14
14
|
import { DRAFT_META_NAME } from "../admin/editor/canvas-diagnosis.js";
|
|
15
15
|
import { fixedPathFor } from "../localize.js";
|
|
16
16
|
import { decodeSegment, joinPath } from "../paths.js";
|
|
17
17
|
import { renderPage } from "../render.js";
|
|
18
|
-
import {
|
|
18
|
+
import { globalUnit, hitUnit } from "../reads.js";
|
|
19
19
|
import { toEntryItem } from "../read-shapes.js";
|
|
20
20
|
import { pageContextOf } from "../site.js";
|
|
21
21
|
import { metadataFromSeo } from "./meta.js";
|
|
22
22
|
import { previewPill } from "./preview-pill.js";
|
|
23
|
+
import { nextReads } from "./reads.js";
|
|
23
24
|
/** Logged once per renderer when Next's cacheComponents is on (spec 2026-09-27 cache components §2). */
|
|
24
25
|
export const CACHE_COMPONENTS_WARNING = "smoodly: Next's cacheComponents is on, and Smoodly does not support it — a missing page answers 200 with noindex instead of 404, and a redirect answers 200 instead of 308. Turn cacheComponents off in next.config.ts; the Next.js guide's \"Cache Components\" section says why (https://smoodly.io/docs/frameworks/nextjs).";
|
|
25
26
|
/** The canvas net (spec 2026-09-27 section guard §5): Server Components hand a section that failed to the
|
|
@@ -31,18 +32,11 @@ export function createPageRenderer(options) {
|
|
|
31
32
|
const href = options.href ?? ((path) => path);
|
|
32
33
|
const shapes = new Map(options.pages.filter((p) => p.schema.kind === "page").map((p) => [p.schema.name, p]));
|
|
33
34
|
const entryPages = new Map(options.pages.filter((p) => p.schema.kind === "entryPage" && p.schema.collection).map((p) => [p.schema.collection, p]));
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Keyed by what the unit holds, not only where: an address that changes
|
|
40
|
-
// owner — a deploy handing it to a code record, which fires no tag —
|
|
41
|
-
// can never serve the previous occupant (spec 2026-09-29 pre-install fixes §5).
|
|
42
|
-
return unstable_cache(() => site.load(target, locale, path), ["smoodly-site", site.cacheKey, locale, path, target.kind, target.id], {
|
|
43
|
-
tags: [target.kind === "page" ? pageTag(target.id) : entryTag(target.id)],
|
|
44
|
-
})();
|
|
45
|
-
}
|
|
35
|
+
/** Next's miss is locate, then one lookup: `unstable_cache` takes its tags before the read, so the id must be
|
|
36
|
+
* known first (spec 2026-10-07 delivery model, decision 6; Astro tags after the read, ../astro/shell.ts).
|
|
37
|
+
* The hit for a located target (locate is uncached: the id it returns IS the cache tag): the read
|
|
38
|
+
* layer's unit, run by Next's adapter — the draft inside draft mode, one cached unit outside it. */
|
|
39
|
+
const load = (target, locale, path, version) => nextReads.run(hitUnit(site, target, locale, path, version));
|
|
46
40
|
/** A redirect row (spec 2026-09-26 redirects §7): the node's CURRENT path in this locale, through href, the query kept; no path there is a 404. */
|
|
47
41
|
async function redirectTo(target, locale, params) {
|
|
48
42
|
const current = (await site.targetPaths(target))[locale];
|
|
@@ -67,7 +61,7 @@ export function createPageRenderer(options) {
|
|
|
67
61
|
notFound();
|
|
68
62
|
}
|
|
69
63
|
const page = pageContextOf(hit, href);
|
|
70
|
-
return renderPage(Page, hit.tree, { sections: options.sections, elements: options.elements, styles: site.styles, custom: site.custom }, { editor, page, rethrow: unstable_rethrow, editorWrap: canvasWrap });
|
|
64
|
+
return renderPage(Page, hit.tree, { sections: options.sections, elements: options.elements, styles: site.styles, custom: { ...site.custom, RichText: (options.RichText ?? site.custom.RichText) } }, { editor, page, rethrow: unstable_rethrow, editorWrap: canvasWrap });
|
|
71
65
|
}
|
|
72
66
|
const View = entryPages.get(hit.collection);
|
|
73
67
|
if (!View) {
|
|
@@ -119,24 +113,23 @@ export function createPageRenderer(options) {
|
|
|
119
113
|
if (target?.redirect)
|
|
120
114
|
await redirectTo(target, locale, params);
|
|
121
115
|
// A code-owned address with no record yet is served from code (spec 2026-09-30 tester findings §2).
|
|
122
|
-
const hit = target ? await load(target, locale, path,
|
|
116
|
+
const hit = target ? await load(target, locale, path, version) : site.codePage(locale, path);
|
|
123
117
|
if (!hit || !accept(hit))
|
|
124
118
|
notFound();
|
|
125
119
|
// The draft marker tells the page editor's canvas diagnosis that Smoodly
|
|
126
120
|
// rendered this response (spec 2026-09-27 shadowed-page detector §3);
|
|
127
121
|
// React hoists the meta into the head. Never outside draft mode.
|
|
128
|
-
return (_jsxs(_Fragment, { children: [draft ? _jsx("meta", { name: DRAFT_META_NAME }) : null, editor ? _jsx(CanvasBridgeSlot, {}) : null, draft && !editor && !version ? previewPill({ route: options.previewRoute }) : null, render(hit, editor, locale)] }));
|
|
122
|
+
return (_jsxs(_Fragment, { children: [draft ? _jsx("meta", { name: DRAFT_META_NAME }) : null, editor ? _jsx("style", { "data-smoodly-bridge": "", children: BRIDGE_CSS }) : null, editor ? _jsx(CanvasBridgeSlot, {}) : null, draft && !editor && !version ? previewPill({ route: options.previewRoute }) : null, render(hit, editor, locale)] }));
|
|
129
123
|
}
|
|
130
124
|
async function metadata(pathFor, opts, accept) {
|
|
131
125
|
// generateMetadata runs alongside the page render, so it gates too.
|
|
132
126
|
await connection();
|
|
133
127
|
const locale = opts?.locale ?? site.locales.default;
|
|
134
128
|
const path = pathFor(locale);
|
|
135
|
-
const { isEnabled: draft } = await draftMode();
|
|
136
129
|
const target = await site.locate(locale, path);
|
|
137
130
|
if (target?.redirect)
|
|
138
131
|
return {};
|
|
139
|
-
const hit = target ? await load(target, locale, path
|
|
132
|
+
const hit = target ? await load(target, locale, path) : site.codePage(locale, path);
|
|
140
133
|
if (!hit || !accept(hit))
|
|
141
134
|
return {};
|
|
142
135
|
// A page type without seo: true still gets its page title as <title>; seo: true adds the panel's values on top.
|
|
@@ -156,17 +149,10 @@ export function createPageRenderer(options) {
|
|
|
156
149
|
* error surfaces as a rejected promise like every other failure. */
|
|
157
150
|
const fixedPath = (Page) => (locale) => fixedPathFor(Page.schema, locale, site.locales, site.homePageSlug);
|
|
158
151
|
const literal = (segments) => () => joinPath(segments.map(decodeSegment));
|
|
159
|
-
/** A global item for a layout:
|
|
160
|
-
* global tag outside draft mode, the live draft inside it. */
|
|
152
|
+
/** A global item for a layout, by schema: the unit `q.getGlobal` runs for the same item and locale. */
|
|
161
153
|
async function getSmoodlyGlobal(item, opts) {
|
|
162
154
|
await connection();
|
|
163
|
-
|
|
164
|
-
const { isEnabled: draft } = await draftMode();
|
|
165
|
-
if (draft)
|
|
166
|
-
return site.getGlobal(item, { locale, draft: true });
|
|
167
|
-
return unstable_cache(() => site.getGlobal(item, { locale }), ["smoodly-global", site.cacheKey, item.name, locale], {
|
|
168
|
-
tags: [globalTag(item.name)],
|
|
169
|
-
})();
|
|
155
|
+
return nextReads.run(globalUnit(site, item, opts?.locale ?? site.locales.default));
|
|
170
156
|
}
|
|
171
157
|
return {
|
|
172
158
|
getSmoodlyGlobal,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function PreviewPillDefine(): null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// Defines the preview pill's element in a Next app (plan 2026-10-03 plain bridge). Loaded lazily through
|
|
3
|
+
// the canvas stub, in draft mode only: after a full load and after a client navigation alike, which an
|
|
4
|
+
// inline script could not do (React never runs a script it inserts on the client).
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
import { definePreviewPill } from "../bridge/preview-pill.js";
|
|
7
|
+
export function PreviewPillDefine() {
|
|
8
|
+
useEffect(() => { definePreviewPill(); }, []);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
|
-
/** The pill's whole client side. `closed` lives in the closure, so a hidden
|
|
3
|
-
* pill stays hidden across client navigations and returns on a reload. */
|
|
4
|
-
export declare const PREVIEW_PILL_SCRIPT: string;
|
|
5
2
|
/** The pill for a route that does not go through the renderer: render it
|
|
6
3
|
* when `(await draftMode()).isEnabled`, never in the editor's canvas. */
|
|
7
4
|
export declare function previewPill(opts?: {
|
|
@@ -1,56 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// The preview pill (spec 2026-09-29 pre-install fixes §2
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// no client module, never in the site's bundles (the live-page rule, §1).
|
|
7
|
-
// The renderer emits it only in draft mode, so a live page's HTML is
|
|
8
|
-
// unchanged. A strict Content-Security-Policy blocks the inline script;
|
|
9
|
-
// the Next.js guide says so. The script is a constant: the route rides on
|
|
10
|
-
// the element, so nothing is interpolated into code.
|
|
2
|
+
// The preview pill in a Next app (spec 2026-09-29 pre-install fixes §2; plan 2026-10-03 plain bridge):
|
|
3
|
+
// the element, which carries the preview route, and the slot that defines it (bridge/preview-pill.ts)
|
|
4
|
+
// from a lazy chunk. The renderer emits both only in draft mode, outside the editor's canvas, so a live
|
|
5
|
+
// page's HTML is unchanged and it loads nothing for the pill.
|
|
11
6
|
import { createElement } from "react";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"border-radius:999px;background:#151727;color:#fff;font:500 13px/1.2 system-ui,-apple-system,sans-serif;" +
|
|
15
|
-
"box-shadow:0 4px 16px rgba(0,0,0,.25);cursor:grab;user-select:none;touch-action:none}" +
|
|
16
|
-
".pill[data-pos=left]{left:16px}.pill[data-pos=center]{left:50%;transform:translateX(-50%)}.pill[data-pos=right]{right:16px}" +
|
|
17
|
-
".pill.dragging{cursor:grabbing}" +
|
|
18
|
-
"a{color:#151727;background:#fff;border-radius:999px;padding:5px 10px;text-decoration:none;font-weight:600}" +
|
|
19
|
-
"button{all:unset;cursor:pointer;width:24px;height:24px;border-radius:999px;display:grid;place-items:center;color:#fff;opacity:.7}" +
|
|
20
|
-
"button:hover,button:focus-visible{opacity:1;outline:2px solid #fff}";
|
|
21
|
-
const HTML = '<div class="pill" role="region" aria-label="Smoodly preview">' +
|
|
22
|
-
"<span>Preview \\u00b7 unpublished changes</span>" +
|
|
23
|
-
'<a href="#">Exit preview</a>' +
|
|
24
|
-
'<button type="button" aria-label="Hide until reload">\\u00d7</button>' +
|
|
25
|
-
"</div>";
|
|
26
|
-
/** The pill's whole client side. `closed` lives in the closure, so a hidden
|
|
27
|
-
* pill stays hidden across client navigations and returns on a reload. */
|
|
28
|
-
export const PREVIEW_PILL_SCRIPT = "(function(){" +
|
|
29
|
-
'if(customElements.get("smoodly-preview-pill"))return;' +
|
|
30
|
-
'var KEY="smoodly-pill-position",closed=false;' +
|
|
31
|
-
'function load(){try{var v=localStorage.getItem(KEY);return v==="left"||v==="right"?v:"center"}catch(e){return"center"}}' +
|
|
32
|
-
"function save(v){try{localStorage.setItem(KEY,v)}catch(e){}}" +
|
|
33
|
-
"class Pill extends HTMLElement{connectedCallback(){" +
|
|
34
|
-
"if(closed){this.hidden=true;return}" +
|
|
35
|
-
"if(this.shadowRoot)return;" +
|
|
36
|
-
'var root=this.attachShadow({mode:"open"}),self=this;' +
|
|
37
|
-
`root.innerHTML="<style>${CSS}</style>${HTML.replace(/"/g, '\\"')}";` +
|
|
38
|
-
'var pill=root.querySelector(".pill"),link=root.querySelector("a"),close=root.querySelector("button");' +
|
|
39
|
-
'pill.setAttribute("data-pos",load());' +
|
|
40
|
-
'link.addEventListener("click",function(e){e.preventDefault();var route=self.getAttribute("data-route")||"/api/preview";' +
|
|
41
|
-
'location.href=route+(route.indexOf("?")<0?"?":"&")+"disable&path="+encodeURIComponent(location.pathname+location.search)});' +
|
|
42
|
-
'close.addEventListener("click",function(){closed=true;self.hidden=true});' +
|
|
43
|
-
"var start=null;" +
|
|
44
|
-
'pill.addEventListener("pointerdown",function(e){if(e.target.closest("a,button"))return;start=e.clientX;pill.setPointerCapture(e.pointerId);pill.classList.add("dragging")});' +
|
|
45
|
-
'pill.addEventListener("pointermove",function(e){if(start!==null)pill.style.translate=(e.clientX-start)+"px 0"});' +
|
|
46
|
-
'pill.addEventListener("pointerup",function(e){if(start===null)return;start=null;pill.classList.remove("dragging");pill.style.translate="";' +
|
|
47
|
-
'var x=e.clientX/innerWidth,pos=x<1/3?"left":x>2/3?"right":"center";pill.setAttribute("data-pos",pos);save(pos)});' +
|
|
48
|
-
'pill.addEventListener("pointercancel",function(){if(start===null)return;start=null;pill.classList.remove("dragging");pill.style.translate=""});' +
|
|
49
|
-
"}}" +
|
|
50
|
-
'customElements.define("smoodly-preview-pill",Pill);' +
|
|
51
|
-
"})();";
|
|
7
|
+
import { PILL_TAG } from "../bridge/preview-pill.js";
|
|
8
|
+
import { PreviewPillSlot } from "./canvas-slot.js";
|
|
52
9
|
/** The pill for a route that does not go through the renderer: render it
|
|
53
10
|
* when `(await draftMode()).isEnabled`, never in the editor's canvas. */
|
|
54
11
|
export function previewPill(opts) {
|
|
55
|
-
return (_jsxs(_Fragment, { children: [_jsx(
|
|
12
|
+
return (_jsxs(_Fragment, { children: [_jsx(PreviewPillSlot, {}), createElement(PILL_TAG, { "data-route": opts?.route ?? "/api/preview" })] }));
|
|
56
13
|
}
|
package/dist/next/queries.d.ts
CHANGED
|
@@ -1,35 +1,10 @@
|
|
|
1
1
|
import type { CollectionSchema } from "../collections.ts";
|
|
2
2
|
import type { GlobalSchema } from "../global.ts";
|
|
3
|
-
import { type
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
export type QueryOptions = {
|
|
7
|
-
locale?: string;
|
|
8
|
-
};
|
|
9
|
-
export type EntriesQuery = Omit<EntryQuery, "locale" | "draft" | "ids"> & QueryOptions;
|
|
10
|
-
type ByName<T, N> = Extract<T, {
|
|
11
|
-
name: N;
|
|
12
|
-
}>;
|
|
13
|
-
/** Fields typed from the schema, references materialized as `EntryRef`s; a widened `CollectionSchema[]` array (names are `string`) falls back to the untyped record instead of `never`. */
|
|
14
|
-
type EntryFields<C extends readonly CollectionSchema<any, string>[], N> = string extends C[number]["name"] ? Record<string, unknown> : {
|
|
15
|
-
[K in keyof NonNullable<ByName<C[number], N>["__entry"]>]: Materialize<NonNullable<ByName<C[number], N>["__entry"]>[K]>;
|
|
16
|
-
};
|
|
17
|
-
/** A read item whose `fields` are the collection's typed values. */
|
|
18
|
-
export type TypedEntryItem<C extends readonly CollectionSchema<any, string>[], N> = EntryItem<EntryFields<C, N>>;
|
|
19
|
-
export type GlobalValue<S extends readonly GlobalSchema<any, string>[], N> = string extends S[number]["name"] ? Record<string, unknown> : NonNullable<ByName<S[number], N>["__value"]>;
|
|
20
|
-
export type SmoodlyQueries<C extends readonly CollectionSchema<any, string>[], S extends readonly GlobalSchema<any, string>[]> = {
|
|
21
|
-
/** The published page tree in a locale, every item with its page fields. */
|
|
22
|
-
getPageTree(options?: QueryOptions): Promise<PageTreeItem[]>;
|
|
23
|
-
/** A page by locale-relative path ("/company/about-us") or id. */
|
|
24
|
-
getPage(pathOrId: string, options?: QueryOptions): Promise<PageItem | null>;
|
|
25
|
-
/** Published entries of a collection: `where` (shared fields), `order`, `limit`, `parent` run in the store. */
|
|
26
|
-
getEntries<N extends C[number]["name"]>(collection: N, options?: EntriesQuery): Promise<TypedEntryItem<C, N>[]>;
|
|
27
|
-
getEntry<N extends C[number]["name"]>(collection: N, idOrPath: string, options?: QueryOptions): Promise<TypedEntryItem<C, N> | null>;
|
|
28
|
-
getGlobal<N extends S[number]["name"]>(item: N, options?: QueryOptions): Promise<GlobalValue<S, N> | null>;
|
|
29
|
-
};
|
|
3
|
+
import { type SmoodlyQueries } from "../reads.ts";
|
|
4
|
+
import type { SmoodlySite } from "../site.ts";
|
|
5
|
+
export type { SmoodlyQueries, QueryOptions, EntriesQuery, TypedEntryItem, GlobalValue } from "../reads.ts";
|
|
30
6
|
export declare function createSmoodlyQueries<const C extends readonly CollectionSchema<any, string>[], const S extends readonly GlobalSchema<any, string>[] = []>(options: {
|
|
31
7
|
site: SmoodlySite;
|
|
32
8
|
collections: C;
|
|
33
9
|
globals?: S;
|
|
34
10
|
}): SmoodlyQueries<C, S>;
|
|
35
|
-
export {};
|