gazetta 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin-dist/assets/index-BO9-CXmW.css +1 -0
- package/admin-dist/assets/index-Ufu8zZH_.js +668 -0
- package/admin-dist/index.html +2 -2
- package/dist/admin-api/error-response.d.ts +21 -0
- package/dist/admin-api/error-response.d.ts.map +1 -0
- package/dist/admin-api/error-response.js +12 -0
- package/dist/admin-api/error-response.js.map +1 -0
- package/dist/admin-api/index.d.ts +0 -2
- package/dist/admin-api/index.d.ts.map +1 -1
- package/dist/admin-api/index.js +4 -24
- package/dist/admin-api/index.js.map +1 -1
- package/dist/admin-api/routes/assets.d.ts +16 -0
- package/dist/admin-api/routes/assets.d.ts.map +1 -0
- package/dist/admin-api/routes/assets.js +433 -0
- package/dist/admin-api/routes/assets.js.map +1 -0
- package/dist/admin-api/routes/fragments.d.ts.map +1 -1
- package/dist/admin-api/routes/fragments.js +30 -4
- package/dist/admin-api/routes/fragments.js.map +1 -1
- package/dist/admin-api/routes/pages.d.ts.map +1 -1
- package/dist/admin-api/routes/pages.js +37 -4
- package/dist/admin-api/routes/pages.js.map +1 -1
- package/dist/admin-api/routes/publish.d.ts.map +1 -1
- package/dist/admin-api/routes/publish.js +68 -35
- package/dist/admin-api/routes/publish.js.map +1 -1
- package/dist/admin-api/schemas/assets.d.ts +48 -0
- package/dist/admin-api/schemas/assets.d.ts.map +1 -0
- package/dist/admin-api/schemas/assets.js +44 -0
- package/dist/admin-api/schemas/assets.js.map +1 -0
- package/dist/admin-api/schemas/index.d.ts +2 -0
- package/dist/admin-api/schemas/index.d.ts.map +1 -1
- package/dist/admin-api/schemas/index.js +2 -0
- package/dist/admin-api/schemas/index.js.map +1 -1
- package/dist/admin-api/source-context.d.ts +0 -7
- package/dist/admin-api/source-context.d.ts.map +1 -1
- package/dist/admin-api/source-context.js +0 -3
- package/dist/admin-api/source-context.js.map +1 -1
- package/dist/assets/analyze-audio.d.ts +3 -0
- package/dist/assets/analyze-audio.d.ts.map +1 -0
- package/dist/assets/analyze-audio.js +80 -0
- package/dist/assets/analyze-audio.js.map +1 -0
- package/dist/assets/analyze-image.d.ts +19 -0
- package/dist/assets/analyze-image.d.ts.map +1 -0
- package/dist/assets/analyze-image.js +123 -0
- package/dist/assets/analyze-image.js.map +1 -0
- package/dist/assets/analyze.d.ts +94 -0
- package/dist/assets/analyze.d.ts.map +1 -0
- package/dist/assets/analyze.js +45 -0
- package/dist/assets/analyze.js.map +1 -0
- package/dist/assets/asset-deps.d.ts +30 -0
- package/dist/assets/asset-deps.d.ts.map +1 -0
- package/dist/assets/asset-deps.js +42 -0
- package/dist/assets/asset-deps.js.map +1 -0
- package/dist/assets/asset-paths.d.ts +155 -0
- package/dist/assets/asset-paths.d.ts.map +1 -0
- package/dist/assets/asset-paths.js +197 -0
- package/dist/assets/asset-paths.js.map +1 -0
- package/dist/assets/delete.d.ts +75 -0
- package/dist/assets/delete.d.ts.map +1 -0
- package/dist/assets/delete.js +82 -0
- package/dist/assets/delete.js.map +1 -0
- package/dist/assets/errors.d.ts +241 -0
- package/dist/assets/errors.d.ts.map +1 -0
- package/dist/assets/errors.js +300 -0
- package/dist/assets/errors.js.map +1 -0
- package/dist/assets/find-refs.d.ts +37 -0
- package/dist/assets/find-refs.d.ts.map +1 -0
- package/dist/assets/find-refs.js +35 -0
- package/dist/assets/find-refs.js.map +1 -0
- package/dist/assets/hash.d.ts +13 -0
- package/dist/assets/hash.d.ts.map +1 -0
- package/dist/assets/hash.js +43 -0
- package/dist/assets/hash.js.map +1 -0
- package/dist/assets/image-metadata.d.ts +11 -0
- package/dist/assets/image-metadata.d.ts.map +1 -0
- package/dist/assets/image-metadata.js +31 -0
- package/dist/assets/image-metadata.js.map +1 -0
- package/dist/assets/ingest-locale.d.ts +86 -0
- package/dist/assets/ingest-locale.d.ts.map +1 -0
- package/dist/assets/ingest-locale.js +209 -0
- package/dist/assets/ingest-locale.js.map +1 -0
- package/dist/assets/ingest.d.ts +96 -0
- package/dist/assets/ingest.d.ts.map +1 -0
- package/dist/assets/ingest.js +308 -0
- package/dist/assets/ingest.js.map +1 -0
- package/dist/assets/kind-compat.d.ts +34 -0
- package/dist/assets/kind-compat.d.ts.map +1 -0
- package/dist/assets/kind-compat.js +33 -0
- package/dist/assets/kind-compat.js.map +1 -0
- package/dist/assets/list.d.ts +46 -0
- package/dist/assets/list.d.ts.map +1 -0
- package/dist/assets/list.js +102 -0
- package/dist/assets/list.js.map +1 -0
- package/dist/assets/manifest-default.d.ts +56 -0
- package/dist/assets/manifest-default.d.ts.map +1 -0
- package/dist/assets/manifest-default.js +120 -0
- package/dist/assets/manifest-default.js.map +1 -0
- package/dist/assets/manifest-filename.d.ts +52 -0
- package/dist/assets/manifest-filename.d.ts.map +1 -0
- package/dist/assets/manifest-filename.js +104 -0
- package/dist/assets/manifest-filename.js.map +1 -0
- package/dist/assets/manifest-locale.d.ts +60 -0
- package/dist/assets/manifest-locale.d.ts.map +1 -0
- package/dist/assets/manifest-locale.js +206 -0
- package/dist/assets/manifest-locale.js.map +1 -0
- package/dist/assets/manifest-merge.d.ts +66 -0
- package/dist/assets/manifest-merge.d.ts.map +1 -0
- package/dist/assets/manifest-merge.js +82 -0
- package/dist/assets/manifest-merge.js.map +1 -0
- package/dist/assets/manifest.d.ts +83 -0
- package/dist/assets/manifest.d.ts.map +1 -0
- package/dist/assets/manifest.js +93 -0
- package/dist/assets/manifest.js.map +1 -0
- package/dist/assets/mime-sniff.d.ts +18 -0
- package/dist/assets/mime-sniff.d.ts.map +1 -0
- package/dist/assets/mime-sniff.js +84 -0
- package/dist/assets/mime-sniff.js.map +1 -0
- package/dist/assets/preprocess-svg.d.ts +3 -0
- package/dist/assets/preprocess-svg.d.ts.map +1 -0
- package/dist/assets/preprocess-svg.js +49 -0
- package/dist/assets/preprocess-svg.js.map +1 -0
- package/dist/assets/preprocess.d.ts +62 -0
- package/dist/assets/preprocess.d.ts.map +1 -0
- package/dist/assets/preprocess.js +86 -0
- package/dist/assets/preprocess.js.map +1 -0
- package/dist/assets/publish-plan.d.ts +41 -0
- package/dist/assets/publish-plan.d.ts.map +1 -0
- package/dist/assets/publish-plan.js +49 -0
- package/dist/assets/publish-plan.js.map +1 -0
- package/dist/assets/publish.d.ts +33 -0
- package/dist/assets/publish.d.ts.map +1 -0
- package/dist/assets/publish.js +81 -0
- package/dist/assets/publish.js.map +1 -0
- package/dist/assets/refs.d.ts +37 -0
- package/dist/assets/refs.d.ts.map +1 -0
- package/dist/assets/refs.js +33 -0
- package/dist/assets/refs.js.map +1 -0
- package/dist/assets/remove-override.d.ts +42 -0
- package/dist/assets/remove-override.d.ts.map +1 -0
- package/dist/assets/remove-override.js +53 -0
- package/dist/assets/remove-override.js.map +1 -0
- package/dist/assets/rename.d.ts +43 -0
- package/dist/assets/rename.d.ts.map +1 -0
- package/dist/assets/rename.js +271 -0
- package/dist/assets/rename.js.map +1 -0
- package/dist/assets/replace.d.ts +37 -0
- package/dist/assets/replace.d.ts.map +1 -0
- package/dist/assets/replace.js +195 -0
- package/dist/assets/replace.js.map +1 -0
- package/dist/assets/resolve.d.ts +141 -0
- package/dist/assets/resolve.d.ts.map +1 -0
- package/dist/assets/resolve.js +381 -0
- package/dist/assets/resolve.js.map +1 -0
- package/dist/assets/rewrite-manifest-asset-ref.d.ts +44 -0
- package/dist/assets/rewrite-manifest-asset-ref.d.ts.map +1 -0
- package/dist/assets/rewrite-manifest-asset-ref.js +51 -0
- package/dist/assets/rewrite-manifest-asset-ref.js.map +1 -0
- package/dist/assets/scan-manifest-for-asset.d.ts +63 -0
- package/dist/assets/scan-manifest-for-asset.d.ts.map +1 -0
- package/dist/assets/scan-manifest-for-asset.js +105 -0
- package/dist/assets/scan-manifest-for-asset.js.map +1 -0
- package/dist/assets/serve-route.d.ts +45 -0
- package/dist/assets/serve-route.d.ts.map +1 -0
- package/dist/assets/serve-route.js +123 -0
- package/dist/assets/serve-route.js.map +1 -0
- package/dist/assets/svg-sanitize.d.ts +38 -0
- package/dist/assets/svg-sanitize.d.ts.map +1 -0
- package/dist/assets/svg-sanitize.js +209 -0
- package/dist/assets/svg-sanitize.js.map +1 -0
- package/dist/assets/update-metadata.d.ts +61 -0
- package/dist/assets/update-metadata.d.ts.map +1 -0
- package/dist/assets/update-metadata.js +82 -0
- package/dist/assets/update-metadata.js.map +1 -0
- package/dist/assets/url.d.ts +82 -0
- package/dist/assets/url.d.ts.map +1 -0
- package/dist/assets/url.js +103 -0
- package/dist/assets/url.js.map +1 -0
- package/dist/assets/validate.d.ts +74 -0
- package/dist/assets/validate.d.ts.map +1 -0
- package/dist/assets/validate.js +136 -0
- package/dist/assets/validate.js.map +1 -0
- package/dist/assets/variants.d.ts +23 -0
- package/dist/assets/variants.d.ts.map +1 -0
- package/dist/assets/variants.js +74 -0
- package/dist/assets/variants.js.map +1 -0
- package/dist/cli/assets-cli.d.ts +58 -0
- package/dist/cli/assets-cli.d.ts.map +1 -0
- package/dist/cli/assets-cli.js +233 -0
- package/dist/cli/assets-cli.js.map +1 -0
- package/dist/cli/assets-display.d.ts +112 -0
- package/dist/cli/assets-display.d.ts.map +1 -0
- package/dist/cli/assets-display.js +106 -0
- package/dist/cli/assets-display.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +0 -2
- package/dist/cli/bootstrap.d.ts.map +1 -1
- package/dist/cli/bootstrap.js +0 -1
- package/dist/cli/bootstrap.js.map +1 -1
- package/dist/cli/index.js +64 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +15 -12
- package/dist/compare.js.map +1 -1
- package/dist/dep-sidecars.d.ts +127 -0
- package/dist/dep-sidecars.d.ts.map +1 -0
- package/dist/dep-sidecars.js +122 -0
- package/dist/dep-sidecars.js.map +1 -0
- package/dist/editor/AssetEmbeddedWidget.d.ts +3 -0
- package/dist/editor/AssetEmbeddedWidget.d.ts.map +1 -0
- package/dist/editor/AssetEmbeddedWidget.js +146 -0
- package/dist/editor/AssetEmbeddedWidget.js.map +1 -0
- package/dist/editor/mount.d.ts +12 -1
- package/dist/editor/mount.d.ts.map +1 -1
- package/dist/editor/mount.js +36 -5
- package/dist/editor/mount.js.map +1 -1
- package/dist/format.d.ts +44 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +65 -0
- package/dist/format.js.map +1 -0
- package/dist/fragment-deps.d.ts +24 -0
- package/dist/fragment-deps.d.ts.map +1 -0
- package/dist/fragment-deps.js +20 -0
- package/dist/fragment-deps.js.map +1 -0
- package/dist/hash.d.ts +0 -6
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +0 -18
- package/dist/hash.js.map +1 -1
- package/dist/history-provider.d.ts.map +1 -1
- package/dist/history-provider.js +30 -8
- package/dist/history-provider.js.map +1 -1
- package/dist/history-recorder.d.ts +7 -3
- package/dist/history-recorder.d.ts.map +1 -1
- package/dist/history-recorder.js +9 -1
- package/dist/history-recorder.js.map +1 -1
- package/dist/history-restorer.d.ts.map +1 -1
- package/dist/history-restorer.js +34 -2
- package/dist/history-restorer.js.map +1 -1
- package/dist/history.d.ts +26 -8
- package/dist/history.d.ts.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +20 -0
- package/dist/locale.d.ts.map +1 -1
- package/dist/locale.js +38 -0
- package/dist/locale.js.map +1 -1
- package/dist/providers/_atomic-write.d.ts +9 -0
- package/dist/providers/_atomic-write.d.ts.map +1 -0
- package/dist/providers/_atomic-write.js +72 -0
- package/dist/providers/_atomic-write.js.map +1 -0
- package/dist/providers/_rm-ignore-missing.d.ts +31 -0
- package/dist/providers/_rm-ignore-missing.d.ts.map +1 -0
- package/dist/providers/_rm-ignore-missing.js +12 -0
- package/dist/providers/_rm-ignore-missing.js.map +1 -0
- package/dist/providers/_stream-interop.d.ts +23 -0
- package/dist/providers/_stream-interop.d.ts.map +1 -0
- package/dist/providers/_stream-interop.js +21 -0
- package/dist/providers/_stream-interop.js.map +1 -0
- package/dist/providers/azure-blob.d.ts.map +1 -1
- package/dist/providers/azure-blob.js +60 -0
- package/dist/providers/azure-blob.js.map +1 -1
- package/dist/providers/filesystem.d.ts +4 -0
- package/dist/providers/filesystem.d.ts.map +1 -1
- package/dist/providers/filesystem.js +63 -2
- package/dist/providers/filesystem.js.map +1 -1
- package/dist/providers/s3.d.ts.map +1 -1
- package/dist/providers/s3.js +84 -1
- package/dist/providers/s3.js.map +1 -1
- package/dist/publish-rendered.d.ts +37 -17
- package/dist/publish-rendered.d.ts.map +1 -1
- package/dist/publish-rendered.js +71 -67
- package/dist/publish-rendered.js.map +1 -1
- package/dist/publish.d.ts +13 -12
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +23 -47
- package/dist/publish.js.map +1 -1
- package/dist/resolver.d.ts +12 -2
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +54 -9
- package/dist/resolver.js.map +1 -1
- package/dist/schema/dimensions.d.ts +78 -0
- package/dist/schema/dimensions.d.ts.map +1 -0
- package/dist/schema/dimensions.js +97 -0
- package/dist/schema/dimensions.js.map +1 -0
- package/dist/schema/helpers.d.ts +108 -0
- package/dist/schema/helpers.d.ts.map +1 -0
- package/dist/schema/helpers.js +133 -0
- package/dist/schema/helpers.js.map +1 -0
- package/dist/schema/index.d.ts +27 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +25 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +390 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +25 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/selector-chain.d.ts +63 -0
- package/dist/selector-chain.d.ts.map +1 -0
- package/dist/selector-chain.js +58 -0
- package/dist/selector-chain.js.map +1 -0
- package/dist/sidecars.d.ts +19 -18
- package/dist/sidecars.d.ts.map +1 -1
- package/dist/sidecars.js +70 -62
- package/dist/sidecars.js.map +1 -1
- package/dist/targets.d.ts.map +1 -1
- package/dist/targets.js +15 -37
- package/dist/targets.js.map +1 -1
- package/dist/themes.d.ts +69 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +85 -0
- package/dist/themes.js.map +1 -0
- package/dist/transforms/adapter.d.ts +115 -0
- package/dist/transforms/adapter.d.ts.map +1 -0
- package/dist/transforms/adapter.js +2 -0
- package/dist/transforms/adapter.js.map +1 -0
- package/dist/transforms/cloudflare.d.ts +17 -0
- package/dist/transforms/cloudflare.d.ts.map +1 -0
- package/dist/transforms/cloudflare.js +110 -0
- package/dist/transforms/cloudflare.js.map +1 -0
- package/dist/transforms/index.d.ts +24 -0
- package/dist/transforms/index.d.ts.map +1 -0
- package/dist/transforms/index.js +30 -0
- package/dist/transforms/index.js.map +1 -0
- package/dist/transforms/sharp.d.ts +3 -0
- package/dist/transforms/sharp.d.ts.map +1 -0
- package/dist/transforms/sharp.js +43 -0
- package/dist/transforms/sharp.js.map +1 -0
- package/dist/types.d.ts +125 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +20 -1
- package/admin-dist/assets/index-B6pVot0Y.css +0 -1
- package/admin-dist/assets/index-DniLwxJA.js +0 -609
- package/dist/providers/r2.d.ts +0 -8
- package/dist/providers/r2.d.ts.map +0 -1
- package/dist/providers/r2.js +0 -86
- package/dist/providers/r2.js.map +0 -1
- package/dist/source-sidecars.d.ts +0 -32
- package/dist/source-sidecars.d.ts.map +0 -1
- package/dist/source-sidecars.js +0 -98
- package/dist/source-sidecars.js.map +0 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse-rooted dependency sidecars — per-edge zero-byte index for
|
|
3
|
+
* "which items depend on this target?" queries.
|
|
4
|
+
*
|
|
5
|
+
* Shape:
|
|
6
|
+
* `{root}/.gazetta/{relation-root}/{target}/{encoded-source-item}`
|
|
7
|
+
*
|
|
8
|
+
* Where:
|
|
9
|
+
* - `{relation-root}` is the directory name for the relationship kind
|
|
10
|
+
* (e.g. `asset-refs`, `fragment-deps`)
|
|
11
|
+
* - `{target}` is the depended-upon name (asset name, fragment name, …),
|
|
12
|
+
* slashes encoded via `encodeRefName`
|
|
13
|
+
* - `{encoded-source-item}` is `pages.{name}` or `fragments.{name}`,
|
|
14
|
+
* with `/` → `.` and an optional `:locale` suffix for locale variants
|
|
15
|
+
*
|
|
16
|
+
* Files are zero bytes. Existence is the index.
|
|
17
|
+
*
|
|
18
|
+
* Why per-edge sidecars over an aggregate JSON file:
|
|
19
|
+
* - Multi-instance correctness: two admin instances saving different
|
|
20
|
+
* items both adding refs to `hero` write to *different paths*. No
|
|
21
|
+
* race, no optimistic concurrency, no retry. Granularity solves the
|
|
22
|
+
* write-contention problem an aggregate JSON would face.
|
|
23
|
+
* - Pattern consistency: matches the existing `.gazetta/` namespace
|
|
24
|
+
* for runtime-never-served metadata.
|
|
25
|
+
* - Self-sufficient targets: same shape on source AND target so any
|
|
26
|
+
* target promoted to source is immediately usable.
|
|
27
|
+
*
|
|
28
|
+
* Single responsibility: filename encoding + per-target directory I/O.
|
|
29
|
+
* Save handlers, publish flow, and reindex CLI compose this with their
|
|
30
|
+
* own walks.
|
|
31
|
+
*/
|
|
32
|
+
import type { ContentRoot } from './content-root.js';
|
|
33
|
+
/**
|
|
34
|
+
* Loose manifest shape for `extract` callers. Page/fragment manifests
|
|
35
|
+
* (with their inline-component descendants) all satisfy this. Avoids
|
|
36
|
+
* coupling the generic dep-sidecar module to a specific manifest type.
|
|
37
|
+
*/
|
|
38
|
+
export type ManifestLike = {
|
|
39
|
+
readonly template?: string;
|
|
40
|
+
readonly content?: Record<string, unknown>;
|
|
41
|
+
readonly components?: readonly unknown[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* One kind of reverse dependency: a domain-specific extractor + the
|
|
45
|
+
* directory under `.gazetta/` where its sidecars live.
|
|
46
|
+
*
|
|
47
|
+
* Each shipping relation is a module-level constant (see
|
|
48
|
+
* `assets/asset-deps.ts`, `fragment-deps.ts`). Add a new relation by
|
|
49
|
+
* declaring another constant; this module needs no changes.
|
|
50
|
+
*/
|
|
51
|
+
export interface DepRelation {
|
|
52
|
+
/** Directory name under `.gazetta/`, e.g. `asset-refs`, `fragment-deps`. */
|
|
53
|
+
readonly rootName: string;
|
|
54
|
+
/**
|
|
55
|
+
* Extract the set of target names this manifest depends on. Pure;
|
|
56
|
+
* implementations must not perform I/O.
|
|
57
|
+
*
|
|
58
|
+
* Examples:
|
|
59
|
+
* - asset-refs: walks content for `_asset` refs
|
|
60
|
+
* - fragment-deps: walks components for `@fragment` refs
|
|
61
|
+
* - template-deps (if added): emits the manifest's `template` field
|
|
62
|
+
*/
|
|
63
|
+
readonly extract: (manifest: ManifestLike) => Set<string>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Identity of a depending item. Distinct entries for each locale variant
|
|
67
|
+
* (per design-media.md → i18n: "Each referencing manifest, including
|
|
68
|
+
* locale variants, is a separate entry").
|
|
69
|
+
*/
|
|
70
|
+
export interface ItemRef {
|
|
71
|
+
source: 'page' | 'fragment';
|
|
72
|
+
/** Bare item name, e.g. `home` or `blog/[slug]`. */
|
|
73
|
+
name: string;
|
|
74
|
+
/** Locale code for locale variants; absent for the default-locale manifest. */
|
|
75
|
+
locale?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Encode an `ItemRef` into a sidecar filename.
|
|
79
|
+
* { source: 'page', name: 'home' } → 'pages.home'
|
|
80
|
+
* { source: 'page', name: 'blog/[slug]' } → 'pages.blog.[slug]'
|
|
81
|
+
* { source: 'fragment', name: 'header', locale: 'fr' } → 'fragments.header:fr'
|
|
82
|
+
*/
|
|
83
|
+
export declare function itemRefToFilename(ref: ItemRef): string;
|
|
84
|
+
/**
|
|
85
|
+
* Parse a sidecar filename back to an `ItemRef`. Returns null for any
|
|
86
|
+
* filename that doesn't match the encoding shape so unrelated files
|
|
87
|
+
* accidentally placed in the directory don't poison reads.
|
|
88
|
+
*/
|
|
89
|
+
export declare function filenameToItemRef(filename: string): ItemRef | null;
|
|
90
|
+
/** Directory path for one target's sidecars under a relation. */
|
|
91
|
+
export declare function depDir(rel: DepRelation, contentRoot: ContentRoot, targetName: string): string;
|
|
92
|
+
/** Path of one sidecar file inside its target's directory. */
|
|
93
|
+
export declare function depSidecarPath(rel: DepRelation, contentRoot: ContentRoot, targetName: string, item: ItemRef): string;
|
|
94
|
+
/**
|
|
95
|
+
* Read all `ItemRef`s currently sidecar-indexed for `targetName`.
|
|
96
|
+
* Returns empty when the directory is missing (no deps, or freshly-
|
|
97
|
+
* created site).
|
|
98
|
+
*/
|
|
99
|
+
export declare function readDepsFor(rel: DepRelation, contentRoot: ContentRoot, targetName: string): Promise<ItemRef[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Apply the diff for one item's deps in this relation.
|
|
102
|
+
*
|
|
103
|
+
* For each target in `oldTargets ∪ newTargets`:
|
|
104
|
+
* - If new and not old → write the sidecar file (creating the target dir)
|
|
105
|
+
* - If old and not new → remove the sidecar file
|
|
106
|
+
* - If both or neither → no I/O
|
|
107
|
+
*
|
|
108
|
+
* Sidecar writes are idempotent (zero-byte files at fixed paths), so
|
|
109
|
+
* concurrent writes from multiple admin instances converge to the same
|
|
110
|
+
* final state. The diff is per (item × target): each instance's save
|
|
111
|
+
* updates only the sidecars for the item it just wrote.
|
|
112
|
+
*/
|
|
113
|
+
export declare function applyDepDiff(rel: DepRelation, contentRoot: ContentRoot, item: ItemRef, oldTargets: ReadonlySet<string>, newTargets: ReadonlySet<string>): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Rebuild the deps sidecars for one item by diffing old vs new manifest
|
|
116
|
+
* via the relation's `extract` function.
|
|
117
|
+
*
|
|
118
|
+
* Use cases:
|
|
119
|
+
* - Save handler: pass `oldManifest` (loaded for history-recording);
|
|
120
|
+
* module re-extracts both old and new dep sets.
|
|
121
|
+
* - Reindex CLI: pass `oldManifest = null` to write fresh sidecars
|
|
122
|
+
* for every dep the item now references.
|
|
123
|
+
* - Delete handler: pass `newManifest = null` to tear down all this
|
|
124
|
+
* item's sidecars in this relation.
|
|
125
|
+
*/
|
|
126
|
+
export declare function rebuildItemDeps(rel: DepRelation, contentRoot: ContentRoot, item: ItemRef, oldManifest: ManifestLike | null, newManifest: ManifestLike | null): Promise<void>;
|
|
127
|
+
//# sourceMappingURL=dep-sidecars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dep-sidecars.d.ts","sourceRoot":"","sources":["../src/dep-sidecars.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGpD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CACzC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,GAAG,CAAC,MAAM,CAAC,CAAA;CAC1D;AAED;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;IAC3B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAKtD;AAID;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CASlE;AAED,iEAAiE;AACjE,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE7F;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAEpH;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAiBpH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAWf;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,YAAY,GAAG,IAAI,EAChC,WAAW,EAAE,YAAY,GAAG,IAAI,GAC/B,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { encodeRefName } from './hash.js';
|
|
2
|
+
/**
|
|
3
|
+
* Encode an `ItemRef` into a sidecar filename.
|
|
4
|
+
* { source: 'page', name: 'home' } → 'pages.home'
|
|
5
|
+
* { source: 'page', name: 'blog/[slug]' } → 'pages.blog.[slug]'
|
|
6
|
+
* { source: 'fragment', name: 'header', locale: 'fr' } → 'fragments.header:fr'
|
|
7
|
+
*/
|
|
8
|
+
export function itemRefToFilename(ref) {
|
|
9
|
+
const prefix = ref.source === 'page' ? 'pages' : 'fragments';
|
|
10
|
+
const encodedName = encodeRefName(ref.name);
|
|
11
|
+
const base = `${prefix}.${encodedName}`;
|
|
12
|
+
return ref.locale ? `${base}:${ref.locale}` : base;
|
|
13
|
+
}
|
|
14
|
+
const FILENAME_RE = /^(pages|fragments)\.(.+?)(?::([a-z]{2}(?:-[a-z]+)?))?$/;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a sidecar filename back to an `ItemRef`. Returns null for any
|
|
17
|
+
* filename that doesn't match the encoding shape so unrelated files
|
|
18
|
+
* accidentally placed in the directory don't poison reads.
|
|
19
|
+
*/
|
|
20
|
+
export function filenameToItemRef(filename) {
|
|
21
|
+
const m = FILENAME_RE.exec(filename);
|
|
22
|
+
if (!m)
|
|
23
|
+
return null;
|
|
24
|
+
const source = m[1] === 'pages' ? 'page' : 'fragment';
|
|
25
|
+
// Decode `.` → `/` to recover the original name. encodeRefName rejects
|
|
26
|
+
// dots in input, so this is unambiguous: every `.` came from a slash.
|
|
27
|
+
const name = m[2].replace(/\./g, '/');
|
|
28
|
+
const locale = m[3];
|
|
29
|
+
return locale ? { source, name, locale } : { source, name };
|
|
30
|
+
}
|
|
31
|
+
/** Directory path for one target's sidecars under a relation. */
|
|
32
|
+
export function depDir(rel, contentRoot, targetName) {
|
|
33
|
+
return contentRoot.path('.gazetta', rel.rootName, encodeRefName(targetName));
|
|
34
|
+
}
|
|
35
|
+
/** Path of one sidecar file inside its target's directory. */
|
|
36
|
+
export function depSidecarPath(rel, contentRoot, targetName, item) {
|
|
37
|
+
return contentRoot.path('.gazetta', rel.rootName, encodeRefName(targetName), itemRefToFilename(item));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read all `ItemRef`s currently sidecar-indexed for `targetName`.
|
|
41
|
+
* Returns empty when the directory is missing (no deps, or freshly-
|
|
42
|
+
* created site).
|
|
43
|
+
*/
|
|
44
|
+
export async function readDepsFor(rel, contentRoot, targetName) {
|
|
45
|
+
const dir = depDir(rel, contentRoot, targetName);
|
|
46
|
+
let entries;
|
|
47
|
+
try {
|
|
48
|
+
entries = await contentRoot.storage.readDir(dir);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Missing directory — treat as no deps. Storage providers vary in
|
|
52
|
+
// exact error shape, so we accept any read failure here.
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const refs = [];
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (entry.isDirectory)
|
|
58
|
+
continue;
|
|
59
|
+
const ref = filenameToItemRef(entry.name);
|
|
60
|
+
if (ref)
|
|
61
|
+
refs.push(ref);
|
|
62
|
+
}
|
|
63
|
+
return refs;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Apply the diff for one item's deps in this relation.
|
|
67
|
+
*
|
|
68
|
+
* For each target in `oldTargets ∪ newTargets`:
|
|
69
|
+
* - If new and not old → write the sidecar file (creating the target dir)
|
|
70
|
+
* - If old and not new → remove the sidecar file
|
|
71
|
+
* - If both or neither → no I/O
|
|
72
|
+
*
|
|
73
|
+
* Sidecar writes are idempotent (zero-byte files at fixed paths), so
|
|
74
|
+
* concurrent writes from multiple admin instances converge to the same
|
|
75
|
+
* final state. The diff is per (item × target): each instance's save
|
|
76
|
+
* updates only the sidecars for the item it just wrote.
|
|
77
|
+
*/
|
|
78
|
+
export async function applyDepDiff(rel, contentRoot, item, oldTargets, newTargets) {
|
|
79
|
+
const added = [];
|
|
80
|
+
const removed = [];
|
|
81
|
+
for (const t of newTargets)
|
|
82
|
+
if (!oldTargets.has(t))
|
|
83
|
+
added.push(t);
|
|
84
|
+
for (const t of oldTargets)
|
|
85
|
+
if (!newTargets.has(t))
|
|
86
|
+
removed.push(t);
|
|
87
|
+
// Adds first — order doesn't affect correctness, but adds-before-removes
|
|
88
|
+
// means a transient observer mid-update sees a superset of refs (safe
|
|
89
|
+
// for delete-blocking) rather than a subset.
|
|
90
|
+
await Promise.all(added.map(target => writeSidecar(rel, contentRoot, target, item)));
|
|
91
|
+
await Promise.all(removed.map(target => removeSidecar(rel, contentRoot, target, item)));
|
|
92
|
+
}
|
|
93
|
+
async function writeSidecar(rel, contentRoot, target, item) {
|
|
94
|
+
const dir = depDir(rel, contentRoot, target);
|
|
95
|
+
await contentRoot.storage.mkdir(dir).catch(() => {
|
|
96
|
+
// Already exists — fine.
|
|
97
|
+
});
|
|
98
|
+
await contentRoot.storage.writeFile(depSidecarPath(rel, contentRoot, target, item), '');
|
|
99
|
+
}
|
|
100
|
+
async function removeSidecar(rel, contentRoot, target, item) {
|
|
101
|
+
await contentRoot.storage.rm(depSidecarPath(rel, contentRoot, target, item)).catch(() => {
|
|
102
|
+
// Already gone — fine. rm is idempotent for our purposes.
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Rebuild the deps sidecars for one item by diffing old vs new manifest
|
|
107
|
+
* via the relation's `extract` function.
|
|
108
|
+
*
|
|
109
|
+
* Use cases:
|
|
110
|
+
* - Save handler: pass `oldManifest` (loaded for history-recording);
|
|
111
|
+
* module re-extracts both old and new dep sets.
|
|
112
|
+
* - Reindex CLI: pass `oldManifest = null` to write fresh sidecars
|
|
113
|
+
* for every dep the item now references.
|
|
114
|
+
* - Delete handler: pass `newManifest = null` to tear down all this
|
|
115
|
+
* item's sidecars in this relation.
|
|
116
|
+
*/
|
|
117
|
+
export async function rebuildItemDeps(rel, contentRoot, item, oldManifest, newManifest) {
|
|
118
|
+
const oldTargets = oldManifest ? rel.extract(oldManifest) : new Set();
|
|
119
|
+
const newTargets = newManifest ? rel.extract(newManifest) : new Set();
|
|
120
|
+
await applyDepDiff(rel, contentRoot, item, oldTargets, newTargets);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=dep-sidecars.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dep-sidecars.js","sourceRoot":"","sources":["../src/dep-sidecars.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetEmbeddedWidget.d.ts","sourceRoot":"","sources":["../../src/editor/AssetEmbeddedWidget.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAqC9C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,2CAuFrD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* React rjsf widget for an embedded-asset schema field.
|
|
4
|
+
*
|
|
5
|
+
* Renders the current selection (thumbnail + name) or an empty state, with
|
|
6
|
+
* a "Change" button that calls a picker callback injected via the form
|
|
7
|
+
* context. The callback's body is the admin's `openAssetPicker` (which
|
|
8
|
+
* uses Pinia internally) — this widget depends on the abstraction, not
|
|
9
|
+
* the implementation.
|
|
10
|
+
*
|
|
11
|
+
* Dependency Inversion:
|
|
12
|
+
* - Widget depends on `formContext.onPickAsset`, a Promise-returning fn
|
|
13
|
+
* - The gazetta editor package knows nothing about how the picker is
|
|
14
|
+
* actually implemented (Vue, React, fullscreen, modal — anything goes)
|
|
15
|
+
* - Admin wires its own implementation when it calls `createEditorMount`
|
|
16
|
+
* - Cross-workspace import boundary is respected (no `apps/admin` imports
|
|
17
|
+
* from the `packages/gazetta` package)
|
|
18
|
+
*
|
|
19
|
+
* Asset summary fetch:
|
|
20
|
+
* - On mount (and whenever `_asset` changes) the widget fetches the
|
|
21
|
+
* single-asset summary to render a thumbnail. Missing assets fall back
|
|
22
|
+
* to the empty state — graceful degradation matches the design doc's
|
|
23
|
+
* resolver principle.
|
|
24
|
+
*/
|
|
25
|
+
import React from 'react';
|
|
26
|
+
function buildThumbnailUrl(summary) {
|
|
27
|
+
const ext = extFromMime(summary.mime);
|
|
28
|
+
if (!ext)
|
|
29
|
+
return null;
|
|
30
|
+
return `/assets/${summary.name}-${summary.hash}.${ext}`;
|
|
31
|
+
}
|
|
32
|
+
function extFromMime(mime) {
|
|
33
|
+
switch (mime) {
|
|
34
|
+
case 'image/jpeg':
|
|
35
|
+
return 'jpg';
|
|
36
|
+
case 'image/png':
|
|
37
|
+
return 'png';
|
|
38
|
+
default:
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function AssetEmbeddedWidget(props) {
|
|
43
|
+
const value = (props.value ?? {});
|
|
44
|
+
const assetName = value._asset ?? null;
|
|
45
|
+
const [summary, setSummary] = React.useState(null);
|
|
46
|
+
const [loadError, setLoadError] = React.useState(null);
|
|
47
|
+
// Fetch the current selection's summary for thumbnail + alt. Refetches
|
|
48
|
+
// when the selected name changes (e.g., after picker confirms).
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
if (!assetName) {
|
|
51
|
+
setSummary(null);
|
|
52
|
+
setLoadError(null);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let cancelled = false;
|
|
56
|
+
// The widget mounts inside the admin's origin; relative URLs resolve
|
|
57
|
+
// against the current page (admin SPA served under `/admin/` in prod,
|
|
58
|
+
// `/` in dev-monorepo). The `/admin/api/...` prefix is added by the
|
|
59
|
+
// admin's build — at runtime we fetch relative to the document.
|
|
60
|
+
const base = resolveAdminBase();
|
|
61
|
+
fetch(`${base}/api/assets/${encodeURIComponent(assetName)}`, {
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
})
|
|
64
|
+
.then(async (res) => {
|
|
65
|
+
if (cancelled)
|
|
66
|
+
return;
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
setSummary(null);
|
|
69
|
+
setLoadError(`Asset "${assetName}" could not be loaded`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const data = (await res.json());
|
|
73
|
+
setSummary(data);
|
|
74
|
+
setLoadError(null);
|
|
75
|
+
})
|
|
76
|
+
.catch(err => {
|
|
77
|
+
if (!cancelled) {
|
|
78
|
+
setLoadError(err.message);
|
|
79
|
+
setSummary(null);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return () => {
|
|
83
|
+
cancelled = true;
|
|
84
|
+
};
|
|
85
|
+
}, [assetName]);
|
|
86
|
+
const accept = readAccept(props.schema);
|
|
87
|
+
const thumbnail = summary ? buildThumbnailUrl(summary) : null;
|
|
88
|
+
async function onPick() {
|
|
89
|
+
const onPickAsset = props.formContext?.onPickAsset;
|
|
90
|
+
if (!onPickAsset) {
|
|
91
|
+
// Admin didn't wire a picker implementation — nothing we can do.
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.warn('[asset-widget] No onPickAsset callback provided in formContext');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const picked = await onPickAsset({
|
|
97
|
+
accept,
|
|
98
|
+
currentAssetName: assetName ?? null,
|
|
99
|
+
});
|
|
100
|
+
if (!picked)
|
|
101
|
+
return;
|
|
102
|
+
// Preserve per-reference overrides when the author re-picks.
|
|
103
|
+
props.onChange({ ...value, _asset: picked._asset });
|
|
104
|
+
}
|
|
105
|
+
return (_jsxs("div", { className: "gz-asset-widget", children: [_jsx("div", { className: "gz-asset-widget-preview", children: thumbnail ? (_jsx("img", { src: thumbnail, alt: summary?.alt ?? '' })) : loadError ? (_jsx("div", { className: "gz-asset-widget-error", children: loadError })) : assetName ? (_jsx("div", { className: "gz-asset-widget-empty", children: "Loading\u2026" })) : (_jsx("div", { className: "gz-asset-widget-empty", children: "No asset selected" })) }), _jsxs("div", { className: "gz-asset-widget-body", children: [_jsx("div", { className: "gz-asset-widget-name", children: assetName ?? '—' }), _jsx("button", { type: "button", className: "gz-asset-widget-pick", "data-testid": "asset-widget-pick", onClick: onPick, children: assetName ? 'Change' : 'Pick' })] })] }));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the admin API base URL from the current document. In the
|
|
109
|
+
* monorepo dev setup the admin is served at `/admin/`; in site-project
|
|
110
|
+
* dev it's at `/admin/` too; the admin SPA's document is always mounted
|
|
111
|
+
* under the admin path, so we strip everything after `/admin/` and use
|
|
112
|
+
* that as the base for `/api/...` calls.
|
|
113
|
+
*
|
|
114
|
+
* Keeps the widget free of Vite's `import.meta.env` (which would tie
|
|
115
|
+
* this package to a Vite build and break its Node-only tsc).
|
|
116
|
+
*/
|
|
117
|
+
function resolveAdminBase() {
|
|
118
|
+
if (typeof document === 'undefined')
|
|
119
|
+
return '';
|
|
120
|
+
const href = document.baseURI;
|
|
121
|
+
try {
|
|
122
|
+
const url = new URL(href);
|
|
123
|
+
// Strip trailing slash; the widget prepends its own `/api/...`.
|
|
124
|
+
return url.pathname.replace(/\/$/, '');
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Read the `accept` filter from the JSON Schema's assetOptions metadata.
|
|
132
|
+
* Zod's `.meta({ assetOptions })` emits the options as a sibling key on
|
|
133
|
+
* the field's schema via `z.toJSONSchema()`.
|
|
134
|
+
*/
|
|
135
|
+
function readAccept(schema) {
|
|
136
|
+
if (!schema || typeof schema !== 'object')
|
|
137
|
+
return undefined;
|
|
138
|
+
const opts = schema.assetOptions;
|
|
139
|
+
if (!opts || typeof opts !== 'object')
|
|
140
|
+
return undefined;
|
|
141
|
+
const accept = opts.accept;
|
|
142
|
+
if (Array.isArray(accept) && accept.every(v => typeof v === 'string'))
|
|
143
|
+
return accept;
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=AssetEmbeddedWidget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetEmbeddedWidget.js","sourceRoot":"","sources":["../../src/editor/AssetEmbeddedWidget.tsx"],"names":[],"mappings":"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"}
|
package/dist/editor/mount.d.ts
CHANGED
|
@@ -8,11 +8,22 @@ export interface DefaultEditorFormProps {
|
|
|
8
8
|
/** Current theme — forwarded to custom field widgets */
|
|
9
9
|
theme?: 'dark' | 'light';
|
|
10
10
|
onChange: (content: Record<string, unknown>) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Asset picker callback — invoked by embedded-asset field widgets.
|
|
13
|
+
* Supplied by the admin; the editor package only depends on the
|
|
14
|
+
* abstract Promise-returning shape.
|
|
15
|
+
*/
|
|
16
|
+
onPickAsset?: (options: {
|
|
17
|
+
accept?: string[];
|
|
18
|
+
currentAssetName?: string | null;
|
|
19
|
+
}) => Promise<{
|
|
20
|
+
_asset: string;
|
|
21
|
+
} | null>;
|
|
11
22
|
}
|
|
12
23
|
/**
|
|
13
24
|
* The default @rjsf form editor as a React component.
|
|
14
25
|
* Custom editors can embed this: `<DefaultEditorForm schema={schema} content={content} onChange={onChange} />`
|
|
15
26
|
*/
|
|
16
|
-
export declare function DefaultEditorForm({ schema: jsonSchema, content, onChange, fieldsBaseUrl, theme, }: DefaultEditorFormProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export declare function DefaultEditorForm({ schema: jsonSchema, content, onChange, fieldsBaseUrl, theme, onPickAsset, }: DefaultEditorFormProps): import("react/jsx-runtime").JSX.Element;
|
|
17
28
|
export declare function createEditorMount(jsonSchema: object): EditorMount;
|
|
18
29
|
//# sourceMappingURL=mount.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/editor/mount.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/editor/mount.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA8gC9C,gDAAgD;AAChD,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,qGAAqG;IACrG,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACxB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IACpD;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CACvH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EAAE,UAAU,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,KAAK,EACL,WAAW,GACZ,EAAE,sBAAsB,2CAoGxB;AAMD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CA4BjE"}
|
package/dist/editor/mount.js
CHANGED
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { createRoot } from 'react-dom/client';
|
|
4
4
|
import Form from '@rjsf/core';
|
|
5
5
|
import validator from '@rjsf/validator-ajv8';
|
|
6
|
+
import { AssetEmbeddedWidget } from './AssetEmbeddedWidget.js';
|
|
6
7
|
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
|
|
7
8
|
import { useEditor, EditorContent } from '@tiptap/react';
|
|
8
9
|
import { BubbleMenu } from '@tiptap/react/menus';
|
|
@@ -120,6 +121,27 @@ const STYLES = `
|
|
|
120
121
|
padding: 2rem; text-align: center; color: var(--color-muted); font-size: 0.75rem;
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
/* ---- Asset widget (embeddedAsset schema fields) ---- */
|
|
125
|
+
.gz-editor .gz-asset-widget {
|
|
126
|
+
display: grid; grid-template-columns: 120px 1fr; gap: 0.75rem; align-items: start;
|
|
127
|
+
padding: 0.5rem; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-input-bg);
|
|
128
|
+
}
|
|
129
|
+
.gz-editor .gz-asset-widget-preview {
|
|
130
|
+
aspect-ratio: 1 / 1; border-radius: 4px; overflow: hidden;
|
|
131
|
+
background: color-mix(in srgb, var(--color-muted) 10%, transparent);
|
|
132
|
+
display: flex; align-items: center; justify-content: center;
|
|
133
|
+
}
|
|
134
|
+
.gz-editor .gz-asset-widget-preview img { width: 100%; height: 100%; object-fit: cover; }
|
|
135
|
+
.gz-editor .gz-asset-widget-empty { color: var(--color-muted); font-size: 0.6875rem; text-align: center; padding: 0.5rem; }
|
|
136
|
+
.gz-editor .gz-asset-widget-error { color: var(--color-danger-fg, #c04040); font-size: 0.6875rem; text-align: center; padding: 0.5rem; }
|
|
137
|
+
.gz-editor .gz-asset-widget-body { display: flex; flex-direction: column; justify-content: space-between; gap: 0.5rem; min-height: 100%; }
|
|
138
|
+
.gz-editor .gz-asset-widget-name { font-family: ui-monospace, monospace; font-size: 0.8125rem; word-break: break-word; }
|
|
139
|
+
.gz-editor .gz-asset-widget-pick {
|
|
140
|
+
align-self: flex-start; padding: 0.375rem 0.75rem; font-size: 0.8125rem;
|
|
141
|
+
background: var(--color-primary); color: white; border: none; border-radius: 4px; cursor: pointer;
|
|
142
|
+
}
|
|
143
|
+
.gz-editor .gz-asset-widget-pick:hover { opacity: 0.9; }
|
|
144
|
+
|
|
123
145
|
/* ---- Slug ---- */
|
|
124
146
|
.gz-editor .gz-slug-widget input {
|
|
125
147
|
font-family: 'JetBrains Mono', 'Fira Code', monospace; font-size: 0.8125rem; letter-spacing: 0.02em;
|
|
@@ -309,7 +331,15 @@ function buildUiSchema(jsonSchema) {
|
|
|
309
331
|
const format = prop.format;
|
|
310
332
|
const type = prop.type;
|
|
311
333
|
const customField = prop.field;
|
|
312
|
-
|
|
334
|
+
const assetOptions = prop.assetOptions;
|
|
335
|
+
// Asset fields — detected by the `assetOptions` metadata that
|
|
336
|
+
// `embeddedAsset()` attaches via Zod `.meta()`. Rendered with the
|
|
337
|
+
// asset widget (invokes the picker via formContext.onPickAsset).
|
|
338
|
+
if (assetOptions) {
|
|
339
|
+
ui[name] = { 'ui:widget': 'embedded-asset' };
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
// Custom field — highest priority after asset fields
|
|
313
343
|
if (customField) {
|
|
314
344
|
ui[name] = { 'ui:widget': `custom-field:${customField}` };
|
|
315
345
|
continue;
|
|
@@ -630,6 +660,7 @@ const builtinWidgets = {
|
|
|
630
660
|
slug: SlugWidget,
|
|
631
661
|
code: CodeWidget,
|
|
632
662
|
json: JsonWidget,
|
|
663
|
+
'embedded-asset': AssetEmbeddedWidget,
|
|
633
664
|
};
|
|
634
665
|
/** Build widgets object including any custom field widgets referenced in the schema */
|
|
635
666
|
function buildWidgets(jsonSchema) {
|
|
@@ -656,7 +687,7 @@ const customTemplates = {
|
|
|
656
687
|
* The default @rjsf form editor as a React component.
|
|
657
688
|
* Custom editors can embed this: `<DefaultEditorForm schema={schema} content={content} onChange={onChange} />`
|
|
658
689
|
*/
|
|
659
|
-
export function DefaultEditorForm({ schema: jsonSchema, content, onChange, fieldsBaseUrl, theme, }) {
|
|
690
|
+
export function DefaultEditorForm({ schema: jsonSchema, content, onChange, fieldsBaseUrl, theme, onPickAsset, }) {
|
|
660
691
|
const uiSchema = React.useMemo(() => buildUiSchema(jsonSchema), [jsonSchema]);
|
|
661
692
|
const widgets = React.useMemo(() => buildWidgets(jsonSchema), [jsonSchema]);
|
|
662
693
|
const [formData, setFormData] = React.useState(content);
|
|
@@ -725,7 +756,7 @@ export function DefaultEditorForm({ schema: jsonSchema, content, onChange, field
|
|
|
725
756
|
return updated;
|
|
726
757
|
});
|
|
727
758
|
}, [onChange]);
|
|
728
|
-
const formContext = React.useMemo(() => ({ reorderArray, fieldsBaseUrl, theme }), [reorderArray, fieldsBaseUrl, theme]);
|
|
759
|
+
const formContext = React.useMemo(() => ({ reorderArray, fieldsBaseUrl, theme, onPickAsset }), [reorderArray, fieldsBaseUrl, theme, onPickAsset]);
|
|
729
760
|
return (_jsxs(_Fragment, { children: [_jsx("style", { children: STYLES }), _jsx("div", { className: "gz-editor", children: _jsx(Form, { schema: jsonSchema, uiSchema: uiSchema, formData: formData, onChange: handleChange, validator: validator, widgets: widgets, templates: customTemplates, formContext: formContext, liveValidate: false, omitExtraData: true, noHtml5Validate: true, autoComplete: "off" }) })] }));
|
|
730
761
|
}
|
|
731
762
|
// ---------------------------------------------------------------------------
|
|
@@ -733,13 +764,13 @@ export function DefaultEditorForm({ schema: jsonSchema, content, onChange, field
|
|
|
733
764
|
// ---------------------------------------------------------------------------
|
|
734
765
|
export function createEditorMount(jsonSchema) {
|
|
735
766
|
return {
|
|
736
|
-
mount(el, { content, schema, theme, onChange, fieldsBaseUrl }) {
|
|
767
|
+
mount(el, { content, schema, theme, onChange, fieldsBaseUrl, onPickAsset }) {
|
|
737
768
|
const existing = roots.get(el);
|
|
738
769
|
if (existing)
|
|
739
770
|
existing.unmount();
|
|
740
771
|
const root = createRoot(el);
|
|
741
772
|
roots.set(el, root);
|
|
742
|
-
root.render(_jsx(DefaultEditorForm, { schema: schema ?? jsonSchema, content: content, theme: theme, fieldsBaseUrl: fieldsBaseUrl, onChange: onChange }));
|
|
773
|
+
root.render(_jsx(DefaultEditorForm, { schema: schema ?? jsonSchema, content: content, theme: theme, fieldsBaseUrl: fieldsBaseUrl, onPickAsset: onPickAsset, onChange: onChange }));
|
|
743
774
|
},
|
|
744
775
|
unmount(el) {
|
|
745
776
|
const root = roots.get(el);
|