@drawnagency/primitives 0.1.115 → 0.1.116

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.
Files changed (32) hide show
  1. package/dist/chunk-26V4YNOR.js +108 -0
  2. package/dist/chunk-DKZCHCV3.js +64 -0
  3. package/dist/chunk-FGQXHUAE.js +39 -0
  4. package/dist/{chunk-JXATNKLV.js → chunk-HUOW3ZV2.js} +5 -5
  5. package/dist/{chunk-FHTKSELX.js → chunk-LNQUUC2S.js} +5 -5
  6. package/dist/{chunk-T26HAKW3.js → chunk-MSBHMY3C.js} +34 -34
  7. package/dist/components/editor/index.js +8 -8
  8. package/dist/components/primitives/index.js +7 -7
  9. package/dist/components/sections/brand-guide.js +5 -5
  10. package/dist/components/sections/register-schemas.js +12 -12
  11. package/dist/components/sections/register.js +12 -12
  12. package/dist/deck-builder/index.js +8 -8
  13. package/dist/index.js +12 -12
  14. package/dist/lib/document-refs.d.ts +8 -0
  15. package/dist/lib/document-refs.d.ts.map +1 -1
  16. package/dist/lib/document-refs.js +14 -0
  17. package/dist/lib/index-ops.js +3 -3
  18. package/dist/lib/index.js +12 -12
  19. package/dist/lib/reference-validation.js +4 -33
  20. package/dist/lib/section-check.d.ts +49 -0
  21. package/dist/lib/section-check.d.ts.map +1 -0
  22. package/dist/lib/section-check.js +14 -0
  23. package/dist/lib/site-validation.d.ts +123 -0
  24. package/dist/lib/site-validation.d.ts.map +1 -0
  25. package/dist/lib/site-validation.js +230 -0
  26. package/package.json +14 -1
  27. package/src/lib/document-refs.ts +30 -0
  28. package/src/lib/section-check.ts +166 -0
  29. package/src/lib/site-validation.ts +269 -0
  30. package/dist/{chunk-NX7TFHA4.js → chunk-CEFFP3CH.js} +7 -7
  31. package/dist/{chunk-YV3YSZEB.js → chunk-L7WTERU5.js} +3 -3
  32. package/dist/{chunk-WYOFQHKI.js → chunk-W72BPGR3.js} +3 -3
@@ -0,0 +1,166 @@
1
+ import { decodeHTMLStrict } from "entities";
2
+ import type { ZodError } from "zod";
3
+ import { blockDepth, MAX_BLOCK_DEPTH, LayoutEnvelopeSchema } from "../schemas/block";
4
+ import { getSchema, getRichTextFields } from "./registry";
5
+ import { REFERENCE_TYPE } from "./reference-type";
6
+
7
+ /**
8
+ * The first thing wrong with one section body: schema, then layout envelope,
9
+ * then nesting depth, then a Reference nested in a container. Only ever one — a
10
+ * body failing its schema is not also judged on its layout — so the write
11
+ * path's `errors[0]` stays the first failure it has always reported.
12
+ */
13
+ export type SectionCheckError =
14
+ | { code: "schema"; zodError: ZodError }
15
+ | { code: "layout"; zodError: ZodError }
16
+ | { code: "depth" }
17
+ | { code: "reference-in-container" };
18
+
19
+ /** HTML entities found in one plain-text field, by path relative to the section. */
20
+ export interface EntityFinding {
21
+ path: string;
22
+ entities: string[];
23
+ }
24
+
25
+ export interface SectionCheck {
26
+ /**
27
+ * No schema is registered for the type, so only the nesting bound was
28
+ * checked. Whether that is acceptable (a site-local type) or an unknown type
29
+ * is the caller's call.
30
+ */
31
+ unregistered: boolean;
32
+ error: SectionCheckError | null;
33
+ /** Empty whenever `error` is set. */
34
+ entities: EntityFinding[];
35
+ }
36
+
37
+ /**
38
+ * Judge one section body against `type`'s registered schema — the write path's
39
+ * (`validateContent`) per-section rules, kept here so the site validators can
40
+ * apply the same ones.
41
+ * Reads the schema registry only: callers register the schemas first.
42
+ */
43
+ export function checkSection(type: string, body: unknown): SectionCheck {
44
+ const schema = getSchema(type);
45
+ if (!schema) {
46
+ // Nesting is still bounded: MAX_BLOCK_DEPTH guards the renderer, and an
47
+ // unvalidated type can still carry a `children` tree.
48
+ return { unregistered: true, error: blockDepth(body) > MAX_BLOCK_DEPTH ? { code: "depth" } : null, entities: [] };
49
+ }
50
+ const fail = (error: SectionCheckError): SectionCheck => ({ unregistered: false, error, entities: [] });
51
+
52
+ const result = schema.safeParse(body);
53
+ if (!result.success) return fail({ code: "schema", zodError: result.error });
54
+ // The per-type schema doesn't declare the top-level `layout` envelope (it
55
+ // lives beside `type`/`content`/`options` in the section file), so validate
56
+ // it separately — the write path preserves it.
57
+ const rawLayout = (body as { layout?: unknown } | null)?.layout;
58
+ if (rawLayout !== undefined) {
59
+ const layoutResult = LayoutEnvelopeSchema.safeParse(rawLayout);
60
+ if (!layoutResult.success) return fail({ code: "layout", zodError: layoutResult.error });
61
+ }
62
+ if (blockDepth(result.data) > MAX_BLOCK_DEPTH) return fail({ code: "depth" });
63
+ // A Reference is expanded into its sources only at the top level of a page;
64
+ // as a container child it would silently render nothing.
65
+ const children = (result.data as { content?: { children?: unknown } }).content?.children;
66
+ if (Array.isArray(children) && children.some((child) => (child as { type?: unknown })?.type === REFERENCE_TYPE)) {
67
+ return fail({ code: "reference-in-container" });
68
+ }
69
+
70
+ const entities: EntityFinding[] = [];
71
+ walkSectionForEntities(type, result.data as { content?: unknown; options?: unknown }, "", (path, tokens) => {
72
+ entities.push({ path, entities: tokens });
73
+ });
74
+ return { unregistered: false, error: null, entities };
75
+ }
76
+
77
+ // Plain-text fields render HTML entities LITERALLY (only registry-declared
78
+ // rich-text fields are HTML) — an agent pasting "’" into a heading ships
79
+ // it verbatim to viewers. A token counts as an entity when it is a numeric
80
+ // character reference or when the HTML5 strict decoder recognizes it — strict
81
+ // decoding requires an exact known name + semicolon, so legitimate text like
82
+ // "R&D;" never matches.
83
+ const ENTITY_RE = /&(#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[a-zA-Z][a-zA-Z0-9]{1,31});/g;
84
+
85
+ function entitiesIn(value: string): string[] {
86
+ const found: string[] = [];
87
+ for (const m of value.matchAll(ENTITY_RE)) {
88
+ const token = m[0];
89
+ if ((m[1].startsWith("#") || decodeHTMLStrict(token) !== token) && !found.includes(token)) {
90
+ found.push(token);
91
+ }
92
+ }
93
+ return found;
94
+ }
95
+
96
+ function visitPlainLeaf(value: unknown, path: string, report: (path: string, entities: string[]) => void): void {
97
+ if (typeof value === "string") {
98
+ const tokens = entitiesIn(value);
99
+ if (tokens.length > 0) report(path, tokens);
100
+ return;
101
+ }
102
+ if (Array.isArray(value)) {
103
+ value.forEach((v, i) => visitPlainLeaf(v, `${path}[${i}]`, report));
104
+ return;
105
+ }
106
+ if (typeof value === "object" && value !== null) {
107
+ for (const [k, v] of Object.entries(value)) visitPlainLeaf(v, `${path}.${k}`, report);
108
+ }
109
+ }
110
+
111
+ // Mirrors sanitizeSectionContent's traversal: rich-text fields are top-level
112
+ // keys of a block's `content` (per type), and `content.children` holds nested
113
+ // blocks that carry their own type. Everything else — including the whole
114
+ // `options` subtree (plain-text config like the colors section's label) — is
115
+ // plain text. Reported paths are relative to the section object itself
116
+ // ("content.heading", "options.label", "content.children[0].content.heading").
117
+ function walkSectionForEntities(
118
+ type: string,
119
+ block: { content?: unknown; options?: unknown },
120
+ prefix: string,
121
+ report: (path: string, entities: string[]) => void,
122
+ ): void {
123
+ const { content, options } = block;
124
+ if (typeof content === "object" && content !== null) {
125
+ const rich = new Set(getRichTextFields(type));
126
+ for (const [key, value] of Object.entries(content as Record<string, unknown>)) {
127
+ if (rich.has(key)) continue;
128
+ if (key === "children" && Array.isArray(value)) {
129
+ value.forEach((child, i) => {
130
+ const c = child as { type?: unknown } | null;
131
+ if (c && typeof c === "object" && typeof c.type === "string") {
132
+ walkSectionForEntities(c.type, c as { content?: unknown; options?: unknown }, `${prefix}content.children[${i}].`, report);
133
+ }
134
+ });
135
+ continue;
136
+ }
137
+ visitPlainLeaf(value, `${prefix}content.${key}`, report);
138
+ }
139
+ }
140
+ if (typeof options === "object" && options !== null) {
141
+ visitPlainLeaf(options, `${prefix}options`, report);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * The write path's sentence for a section check's error, shared with
147
+ * validate_site so a dry run words a failure exactly as the save it predicts.
148
+ */
149
+ export function sectionCheckMessage(sectionId: string, type: string, code: SectionCheckError["code"]): string {
150
+ switch (code) {
151
+ case "schema":
152
+ return `Validation failed: section "${sectionId}" (${type})`;
153
+ case "layout":
154
+ return `Validation failed: section "${sectionId}" has an invalid layout envelope`;
155
+ case "depth":
156
+ return `Validation failed: section "${sectionId}" exceeds max nesting depth (${MAX_BLOCK_DEPTH})`;
157
+ case "reference-in-container":
158
+ return `Validation failed: section "${sectionId}" holds a Reference, which can only sit at the top level of a page, not inside a container`;
159
+ }
160
+ }
161
+
162
+ /** The write path's warning for one entity finding, shared with validate_site. */
163
+ export function entityWarning(sectionId: string, { path, entities }: EntityFinding): string {
164
+ const noun = entities.length > 1 ? "entities" : "entity";
165
+ return `section "${sectionId}": "${path}" contains HTML ${noun} ${entities.join(", ")} — this is a plain-text field, so entities render literally; if a character was intended, use the character itself.`;
166
+ }
@@ -0,0 +1,269 @@
1
+ import type { ZodType } from "zod";
2
+ import { IndexSchema, SiteConfigSchema, type SiteIndex } from "../schemas/site-config";
3
+ import { ImageManifestSchema } from "../schemas/media";
4
+ import { DOCUMENT_MANIFEST_PATH, DocumentManifestSchema } from "../schemas/documents";
5
+ import { NAV, TAGS, readSidecarText, type SidecarDefinition, type SidecarDiagnostic } from "./sidecars";
6
+ import { checkSection } from "./section-check";
7
+ import { upgradeLegacySection } from "./migrate-sections-transform";
8
+ import { collectImageIds } from "./image-refs";
9
+ import { collectDocumentIds } from "./document-refs";
10
+ import { referenceProblems, type ReferenceProblem } from "./reference-validation";
11
+
12
+ const INDEX_PATH = "src/content/index.json";
13
+ const SITE_CONFIG_PATH = "src/content/site-config.json";
14
+ const IMAGE_MANIFEST_PATH = "src/content/image-manifest.json";
15
+ const sectionPath = (id: string): string => `src/content/sections/${id}.json`;
16
+
17
+ /** What the caller can vouch for. Never how severe anything is — that is SEVERITY. */
18
+ export interface SiteValidationPolicy {
19
+ /**
20
+ * A section type with no registered schema: `trust` reads it as a real type
21
+ * this caller cannot check (the connector, whose branch index is the proof; a
22
+ * CLI whose site sections module failed to load), `reject` as an unknown type.
23
+ */
24
+ unregisteredTypes: "trust" | "reject";
25
+ }
26
+
27
+ export interface SchemaIssue {
28
+ path: readonly PropertyKey[];
29
+ message: string;
30
+ }
31
+
32
+ type ProblemBody =
33
+ | { code: "index-missing" }
34
+ | { code: "index-invalid"; detail: string }
35
+ | { code: "site-id-empty" }
36
+ | { code: "site-config-missing" }
37
+ | { code: "site-config-invalid"; detail: string }
38
+ | { code: "sidecar-degraded"; sidecar: "nav" | "tags" }
39
+ | { code: "sidecar-repaired"; sidecar: "nav" | "tags"; diagnostic: SidecarDiagnostic }
40
+ | { code: "image-manifest-invalid"; detail: string }
41
+ | { code: "document-manifest-invalid"; detail: string }
42
+ | { code: "section-missing"; sectionId: string }
43
+ | { code: "section-unparseable"; sectionId: string; detail: string }
44
+ | { code: "section-id-mismatch"; sectionId: string; fileId: unknown }
45
+ | { code: "section-type-mismatch"; sectionId: string; fileType: unknown; indexType: string }
46
+ | { code: "unknown-type"; sectionId: string; type: string }
47
+ | { code: "unvalidated-type"; sectionId: string; type: string }
48
+ | { code: "section-invalid"; sectionId: string; type: string; issues: SchemaIssue[] }
49
+ | { code: "layout-invalid"; sectionId: string; issues: SchemaIssue[] }
50
+ | { code: "too-deep"; sectionId: string }
51
+ | { code: "reference-in-container"; sectionId: string }
52
+ | { code: "html-entities"; sectionId: string; path: string; entities: string[] }
53
+ | { code: "dangling-image"; sectionId: string; imageId: string }
54
+ | { code: "dangling-document"; sectionId: string; documentId: string }
55
+ | { code: "reference-problem"; problem: ReferenceProblem };
56
+
57
+ export type SiteProblemCode = ProblemBody["code"];
58
+ export type Severity = "error" | "warning";
59
+ export type SiteProblem = ProblemBody & { severity: Severity };
60
+
61
+ /**
62
+ * The one severity table every caller shares. An ERROR is something the site
63
+ * cannot load, or authored content it silently loses or rejects. A WARNING is
64
+ * something the site renders through — dropping or repairing it itself — and
65
+ * that is fixable in-product, so it must not fail a build.
66
+ */
67
+ export const SEVERITY: Readonly<Record<SiteProblemCode, Severity>> = {
68
+ "index-missing": "error",
69
+ "index-invalid": "error",
70
+ "site-id-empty": "error",
71
+ "site-config-missing": "error",
72
+ "site-config-invalid": "error",
73
+ // The loader drops what it cannot read, so authored grouping or tags vanish.
74
+ "sidecar-degraded": "error",
75
+ // What reconcile repairs on every load: the runtime has already fixed it.
76
+ "sidecar-repaired": "warning",
77
+ "image-manifest-invalid": "error",
78
+ "document-manifest-invalid": "error",
79
+ "section-missing": "error",
80
+ "section-unparseable": "error",
81
+ "section-id-mismatch": "error",
82
+ "section-type-mismatch": "error",
83
+ "unknown-type": "error",
84
+ "unvalidated-type": "warning",
85
+ "section-invalid": "error",
86
+ "layout-invalid": "error",
87
+ "too-deep": "error",
88
+ "reference-in-container": "error",
89
+ "html-entities": "warning",
90
+ "dangling-image": "warning",
91
+ "dangling-document": "warning",
92
+ "reference-problem": "warning",
93
+ };
94
+
95
+ export interface SiteValidation {
96
+ problems: SiteProblem[];
97
+ /** The section ids the index places on pages; null when the index could not be read. */
98
+ placedIds: string[] | null;
99
+ }
100
+
101
+ function parseJson(text: string): { ok: true; value: unknown } | { ok: false; detail: string } {
102
+ try {
103
+ return { ok: true, value: JSON.parse(text) };
104
+ } catch (e) {
105
+ return { ok: false, detail: e instanceof Error ? e.message : String(e) };
106
+ }
107
+ }
108
+
109
+ /** The text parsed and schema-checked, or why it could not be. */
110
+ function parseFile<T>(text: string, schema: ZodType<T>): { ok: true; data: T } | { ok: false; detail: string } {
111
+ const json = parseJson(text);
112
+ if (!json.ok) return json;
113
+ const parsed = schema.safeParse(json.value);
114
+ return parsed.success ? { ok: true, data: parsed.data } : { ok: false, detail: parsed.error.message };
115
+ }
116
+
117
+ /**
118
+ * Validate a site's committed content. `files` maps repo paths under
119
+ * `src/content/` to their text; an absent path is an absent file. Pure: it reads
120
+ * the schema registry, so callers register built-in (and any site-local)
121
+ * schemas first. Only sections the index places on a page are judged — orphan
122
+ * files are the caller's concern.
123
+ */
124
+ export function validateSite(files: Readonly<Record<string, string>>, policy: SiteValidationPolicy): SiteValidation {
125
+ const problems: SiteProblem[] = [];
126
+ const report = (body: ProblemBody) => problems.push({ ...body, severity: SEVERITY[body.code] });
127
+
128
+ const indexText = files[INDEX_PATH];
129
+ if (indexText === undefined) {
130
+ report({ code: "index-missing" });
131
+ return { problems, placedIds: null };
132
+ }
133
+ const indexRead = parseFile(indexText, IndexSchema);
134
+ if (!indexRead.ok) {
135
+ report({ code: "index-invalid", detail: indexRead.detail });
136
+ return { problems, placedIds: null };
137
+ }
138
+ const index: SiteIndex = indexRead.data;
139
+ const placedIds = [...new Set(index.pages.flatMap((p) => p.order))];
140
+
141
+ if (!index.siteId) report({ code: "site-id-empty" });
142
+
143
+ const configText = files[SITE_CONFIG_PATH];
144
+ if (configText === undefined) {
145
+ report({ code: "site-config-missing" });
146
+ } else {
147
+ const configRead = parseFile(configText, SiteConfigSchema);
148
+ if (!configRead.ok) report({ code: "site-config-invalid", detail: configRead.detail });
149
+ }
150
+
151
+ const checkSidecar = <E>(def: SidecarDefinition<E>): E[] | null => {
152
+ const text = files[def.path];
153
+ if (text === undefined) return [];
154
+ const read = readSidecarText(def, text);
155
+ if (read.degraded) {
156
+ report({ code: "sidecar-degraded", sidecar: def.name });
157
+ return null;
158
+ }
159
+ for (const diagnostic of def.reconcile(read.entries, index).diagnostics) {
160
+ report({ code: "sidecar-repaired", sidecar: def.name, diagnostic });
161
+ }
162
+ return read.entries;
163
+ };
164
+ checkSidecar(NAV);
165
+ // The tag ids a Reference may name: none when the file is absent, unknown
166
+ // (null, so tag ids go unchecked) when it cannot be read in full.
167
+ const tags = checkSidecar(TAGS);
168
+ const tagIds = tags === null ? null : new Set(tags.map((t) => t.id));
169
+
170
+ // The ids in each manifest; null when it is present but unreadable — nothing
171
+ // can be judged against a manifest we could not read, and reporting every
172
+ // reference as dangling would bury the one diagnostic that matters. Absent is
173
+ // the normal state of a site with nothing uploaded: an empty manifest.
174
+ let imageIds: Set<string> | null = new Set();
175
+ const imageText = files[IMAGE_MANIFEST_PATH];
176
+ if (imageText !== undefined) {
177
+ const read = parseFile(imageText, ImageManifestSchema);
178
+ if (read.ok) imageIds = new Set(Object.keys(read.data.images));
179
+ else {
180
+ report({ code: "image-manifest-invalid", detail: read.detail });
181
+ imageIds = null;
182
+ }
183
+ }
184
+ let documentIds: Set<string> | null = new Set();
185
+ const documentText = files[DOCUMENT_MANIFEST_PATH];
186
+ if (documentText !== undefined) {
187
+ const read = parseFile(documentText, DocumentManifestSchema);
188
+ if (read.ok) documentIds = new Set(Object.keys(read.data));
189
+ else {
190
+ report({ code: "document-manifest-invalid", detail: read.detail });
191
+ documentIds = null;
192
+ }
193
+ }
194
+
195
+ const bodies: { id: string; content: unknown }[] = [];
196
+ for (const id of placedIds) {
197
+ const text = files[sectionPath(id)];
198
+ if (text === undefined) {
199
+ report({ code: "section-missing", sectionId: id });
200
+ continue;
201
+ }
202
+ const json = parseJson(text);
203
+ if (!json.ok) {
204
+ report({ code: "section-unparseable", sectionId: id, detail: json.detail });
205
+ continue;
206
+ }
207
+ const onDisk = (typeof json.value === "object" && json.value !== null ? json.value : {}) as Record<string, unknown>;
208
+ if (onDisk.id !== id) report({ code: "section-id-mismatch", sectionId: id, fileId: onDisk.id });
209
+ // Retired types are rewritten at every load before the site validates them,
210
+ // so validate what the site runs, by the type it runs as.
211
+ const body = upgradeLegacySection(json.value);
212
+ bodies.push({ id, content: body });
213
+ const upgradedType = typeof body === "object" && body !== null ? (body as { type?: unknown }).type : undefined;
214
+ const type = typeof upgradedType === "string" ? upgradedType : "";
215
+
216
+ // The index may name either form of a legacy section: the retired type
217
+ // both still hold, or the type it upgrades to once a writer has reconciled
218
+ // the index. Either renders; only an index type matching neither is wrong.
219
+ const indexType = index.sections[id].type;
220
+ if (indexType !== onDisk.type && indexType !== upgradedType) {
221
+ report({ code: "section-type-mismatch", sectionId: id, fileType: onDisk.type, indexType });
222
+ }
223
+ // A body with no type has just been reported as a mismatch; there is no
224
+ // type to judge it by, and "unknown type \"\"" would say nothing more.
225
+ if (type === "") continue;
226
+
227
+ const check = checkSection(type, body);
228
+ if (check.unregistered) {
229
+ if (policy.unregisteredTypes === "reject") {
230
+ report({ code: "unknown-type", sectionId: id, type });
231
+ } else {
232
+ report({ code: "unvalidated-type", sectionId: id, type });
233
+ if (check.error?.code === "depth") report({ code: "too-deep", sectionId: id });
234
+ }
235
+ } else if (check.error) {
236
+ const error = check.error;
237
+ switch (error.code) {
238
+ case "schema":
239
+ report({ code: "section-invalid", sectionId: id, type, issues: error.zodError.issues });
240
+ break;
241
+ case "layout":
242
+ report({ code: "layout-invalid", sectionId: id, issues: error.zodError.issues });
243
+ break;
244
+ case "depth":
245
+ report({ code: "too-deep", sectionId: id });
246
+ break;
247
+ case "reference-in-container":
248
+ report({ code: "reference-in-container", sectionId: id });
249
+ break;
250
+ }
251
+ }
252
+ for (const { path, entities } of check.entities) report({ code: "html-entities", sectionId: id, path, entities });
253
+
254
+ if (imageIds) {
255
+ for (const imageId of collectImageIds(body)) {
256
+ if (!imageIds.has(imageId)) report({ code: "dangling-image", sectionId: id, imageId });
257
+ }
258
+ }
259
+ if (documentIds) {
260
+ for (const documentId of collectDocumentIds(body)) {
261
+ if (!documentIds.has(documentId)) report({ code: "dangling-document", sectionId: id, documentId });
262
+ }
263
+ }
264
+ }
265
+
266
+ for (const problem of referenceProblems(bodies, index, tagIds)) report({ code: "reference-problem", problem });
267
+
268
+ return { problems, placedIds };
269
+ }
@@ -1,10 +1,3 @@
1
- import {
2
- Kbd,
3
- Tooltip
4
- } from "./chunk-JCDZAFX2.js";
5
- import {
6
- Button
7
- } from "./chunk-GGYY3JQJ.js";
8
1
  import {
9
2
  Checkbox,
10
3
  Tabs
@@ -18,6 +11,13 @@ import {
18
11
  useMediaLibrary,
19
12
  useResolvedMedia
20
13
  } from "./chunk-RIITGTV3.js";
14
+ import {
15
+ Kbd,
16
+ Tooltip
17
+ } from "./chunk-JCDZAFX2.js";
18
+ import {
19
+ Button
20
+ } from "./chunk-GGYY3JQJ.js";
21
21
  import {
22
22
  IconButton
23
23
  } from "./chunk-IIDKW7AV.js";
@@ -1,12 +1,12 @@
1
- import {
2
- computeReorderTarget
3
- } from "./chunk-DIMDRBFK.js";
4
1
  import {
5
2
  DragHandle,
6
3
  DropEdgeIndicator,
7
4
  useMediaLibrary,
8
5
  useResolvedMedia
9
6
  } from "./chunk-RIITGTV3.js";
7
+ import {
8
+ computeReorderTarget
9
+ } from "./chunk-DIMDRBFK.js";
10
10
  import {
11
11
  IconButton
12
12
  } from "./chunk-IIDKW7AV.js";
@@ -1,9 +1,9 @@
1
- import {
2
- getContainerChildren
3
- } from "./chunk-L45X7DKX.js";
4
1
  import {
5
2
  audiencePasses
6
3
  } from "./chunk-VY3IFGE3.js";
4
+ import {
5
+ getContainerChildren
6
+ } from "./chunk-L45X7DKX.js";
7
7
  import {
8
8
  pagesInNavOrder
9
9
  } from "./chunk-DJOGPSRU.js";