@kywi-software/core 0.11.0 → 0.12.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/AGENT-PATTERNS.md +96 -10
- package/dist/admin/content-form.d.ts.map +1 -1
- package/dist/admin/content-form.js +97 -10
- package/dist/admin/content-form.js.map +1 -1
- package/dist/admin/layout-editor/component-actions.d.ts +219 -22
- package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
- package/dist/admin/layout-editor/component-actions.js +356 -36
- package/dist/admin/layout-editor/component-actions.js.map +1 -1
- package/dist/admin/layout-editor/component-fork.d.ts +224 -0
- package/dist/admin/layout-editor/component-fork.d.ts.map +1 -0
- package/dist/admin/layout-editor/component-fork.js +342 -0
- package/dist/admin/layout-editor/component-fork.js.map +1 -0
- package/dist/admin/layout-editor/editor-canvas.d.ts +8 -0
- package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
- package/dist/admin/layout-editor/editor-canvas.js +120 -12
- package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
- package/dist/admin/layout-editor/editor-dialog.d.ts +164 -0
- package/dist/admin/layout-editor/editor-dialog.d.ts.map +1 -0
- package/dist/admin/layout-editor/editor-dialog.js +195 -0
- package/dist/admin/layout-editor/editor-dialog.js.map +1 -0
- package/dist/admin/layout-editor/editor-state.d.ts +76 -0
- package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
- package/dist/admin/layout-editor/editor-state.js +47 -1
- package/dist/admin/layout-editor/editor-state.js.map +1 -1
- package/dist/admin/layout-editor/editor.css +279 -2
- package/dist/admin/layout-editor/index.d.ts +10 -1
- package/dist/admin/layout-editor/index.d.ts.map +1 -1
- package/dist/admin/layout-editor/index.js +11 -1
- package/dist/admin/layout-editor/index.js.map +1 -1
- package/dist/admin/layout-editor/layout-editor.d.ts +58 -1
- package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
- package/dist/admin/layout-editor/layout-editor.js +160 -190
- package/dist/admin/layout-editor/layout-editor.js.map +1 -1
- package/dist/admin/layout-editor/linked-component.d.ts +39 -0
- package/dist/admin/layout-editor/linked-component.d.ts.map +1 -0
- package/dist/admin/layout-editor/linked-component.js +42 -0
- package/dist/admin/layout-editor/linked-component.js.map +1 -0
- package/dist/admin/layout-editor/module-overlay.d.ts +10 -1
- package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/module-overlay.js +33 -4
- package/dist/admin/layout-editor/module-overlay.js.map +1 -1
- package/dist/admin/layout-editor/node-lookup.d.ts +35 -0
- package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -1
- package/dist/admin/layout-editor/node-lookup.js +91 -0
- package/dist/admin/layout-editor/node-lookup.js.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.js +114 -23
- package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
- package/dist/admin/layout-editor/props-panel.d.ts +54 -1
- package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
- package/dist/admin/layout-editor/props-panel.js +118 -9
- package/dist/admin/layout-editor/props-panel.js.map +1 -1
- package/dist/admin/layout-editor/section-overlay.d.ts +9 -1
- package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/section-overlay.js +14 -4
- package/dist/admin/layout-editor/section-overlay.js.map +1 -1
- package/dist/admin/layout-editor/section-template-picker.d.ts +27 -1
- package/dist/admin/layout-editor/section-template-picker.d.ts.map +1 -1
- package/dist/admin/layout-editor/section-template-picker.js +71 -17
- package/dist/admin/layout-editor/section-template-picker.js.map +1 -1
- package/dist/admin/layout-editor/use-component-connect.d.ts +61 -0
- package/dist/admin/layout-editor/use-component-connect.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-connect.js +292 -0
- package/dist/admin/layout-editor/use-component-connect.js.map +1 -0
- package/dist/admin/layout-editor/use-component-fork.d.ts +123 -0
- package/dist/admin/layout-editor/use-component-fork.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-fork.js +346 -0
- package/dist/admin/layout-editor/use-component-fork.js.map +1 -0
- package/dist/admin/layout-editor/use-component-library.d.ts +94 -0
- package/dist/admin/layout-editor/use-component-library.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-library.js +55 -0
- package/dist/admin/layout-editor/use-component-library.js.map +1 -0
- package/dist/admin/registry.d.ts +11 -2
- package/dist/admin/registry.d.ts.map +1 -1
- package/dist/admin/registry.js +40 -4
- package/dist/admin/registry.js.map +1 -1
- package/dist/admin/server.d.ts +1 -0
- package/dist/admin/server.d.ts.map +1 -1
- package/dist/admin/server.js +5 -0
- package/dist/admin/server.js.map +1 -1
- package/dist/admin/styles/admin.css +43 -0
- package/dist/admin/surfaces/components/component-edit.d.ts +3 -0
- package/dist/admin/surfaces/components/component-edit.d.ts.map +1 -0
- package/dist/admin/surfaces/components/component-edit.js +235 -0
- package/dist/admin/surfaces/components/component-edit.js.map +1 -0
- package/dist/admin/surfaces/components/components-data.d.ts +153 -0
- package/dist/admin/surfaces/components/components-data.d.ts.map +1 -0
- package/dist/admin/surfaces/components/components-data.js +154 -0
- package/dist/admin/surfaces/components/components-data.js.map +1 -0
- package/dist/admin/surfaces/components/components-list.d.ts +3 -0
- package/dist/admin/surfaces/components/components-list.d.ts.map +1 -0
- package/dist/admin/surfaces/components/components-list.js +196 -0
- package/dist/admin/surfaces/components/components-list.js.map +1 -0
- package/dist/admin/surfaces/components/definition-canvas.d.ts +73 -0
- package/dist/admin/surfaces/components/definition-canvas.d.ts.map +1 -0
- package/dist/admin/surfaces/components/definition-canvas.js +207 -0
- package/dist/admin/surfaces/components/definition-canvas.js.map +1 -0
- package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layout-editor-client.js +14 -2
- package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-data.d.ts +3 -14
- package/dist/admin/surfaces/layouts/layouts-data.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-data.js +4 -7
- package/dist/admin/surfaces/layouts/layouts-data.js.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-list.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-list.js +16 -22
- package/dist/admin/surfaces/layouts/layouts-list.js.map +1 -1
- package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
- package/dist/admin/surfaces/settings/admin-features.js +1 -0
- package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
- package/dist/api/bulk-routes.d.ts.map +1 -1
- package/dist/api/bulk-routes.js +25 -0
- package/dist/api/bulk-routes.js.map +1 -1
- package/dist/api/changeset-routes.d.ts.map +1 -1
- package/dist/api/changeset-routes.js +17 -3
- package/dist/api/changeset-routes.js.map +1 -1
- package/dist/api/component-routes.d.ts.map +1 -1
- package/dist/api/component-routes.js +228 -8
- package/dist/api/component-routes.js.map +1 -1
- package/dist/api/content-routes.d.ts.map +1 -1
- package/dist/api/content-routes.js +64 -0
- package/dist/api/content-routes.js.map +1 -1
- package/dist/api/feed-routes.d.ts +6 -6
- package/dist/api/graphql/resolvers.d.ts +4 -4
- package/dist/api/layout-routes.d.ts.map +1 -1
- package/dist/api/layout-routes.js +4 -17
- package/dist/api/layout-routes.js.map +1 -1
- package/dist/api/layout-write-guard.d.ts +80 -0
- package/dist/api/layout-write-guard.d.ts.map +1 -0
- package/dist/api/layout-write-guard.js +88 -0
- package/dist/api/layout-write-guard.js.map +1 -0
- package/dist/api/section-template-routes.d.ts.map +1 -1
- package/dist/api/section-template-routes.js +11 -0
- package/dist/api/section-template-routes.js.map +1 -1
- package/dist/api/translation-routes.d.ts.map +1 -1
- package/dist/api/translation-routes.js +8 -0
- package/dist/api/translation-routes.js.map +1 -1
- package/dist/api/version-routes.d.ts.map +1 -1
- package/dist/api/version-routes.js +20 -8
- package/dist/api/version-routes.js.map +1 -1
- package/dist/api/workflow-routes.d.ts.map +1 -1
- package/dist/api/workflow-routes.js +53 -8
- package/dist/api/workflow-routes.js.map +1 -1
- package/dist/db/analytics-queries.d.ts +3 -3
- package/dist/db/apply-snapshot.d.ts +75 -8
- package/dist/db/apply-snapshot.d.ts.map +1 -1
- package/dist/db/apply-snapshot.js +104 -8
- package/dist/db/apply-snapshot.js.map +1 -1
- package/dist/db/audit-queries.d.ts +12 -12
- package/dist/db/category-queries.d.ts +3 -3
- package/dist/db/changeset-audit-queries.d.ts +4 -4
- package/dist/db/changeset-queries.d.ts +7 -7
- package/dist/db/comment-queries.d.ts +8 -8
- package/dist/db/comment-subscription-queries.d.ts +2 -2
- package/dist/db/component-instance-queries.d.ts +197 -0
- package/dist/db/component-instance-queries.d.ts.map +1 -0
- package/dist/db/component-instance-queries.js +405 -0
- package/dist/db/component-instance-queries.js.map +1 -0
- package/dist/db/content-queries.d.ts.map +1 -1
- package/dist/db/content-queries.js +39 -2
- package/dist/db/content-queries.js.map +1 -1
- package/dist/db/extension-queries.d.ts +7 -7
- package/dist/db/feed-queries.d.ts +3 -3
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/layout-queries.d.ts +13 -1
- package/dist/db/layout-queries.d.ts.map +1 -1
- package/dist/db/layout-queries.js +52 -7
- package/dist/db/layout-queries.js.map +1 -1
- package/dist/db/locale-queries.d.ts.map +1 -1
- package/dist/db/locale-queries.js +0 -0
- package/dist/db/locale-queries.js.map +1 -1
- package/dist/db/media-queries.d.ts +12 -12
- package/dist/db/revision-queries.d.ts +3 -3
- package/dist/db/site-admin-queries.d.ts +2 -2
- package/dist/db/site-membership-queries.d.ts +3 -3
- package/dist/db/site-plugin-queries.d.ts +2 -2
- package/dist/db/site-queries.d.ts +6 -6
- package/dist/db/version-queries.d.ts +12 -12
- package/dist/db/webhook-queries.d.ts +11 -11
- package/dist/db/workflow-queries.d.ts +6 -6
- package/dist/db/workflow-queries.d.ts.map +1 -1
- package/dist/db/workflow-queries.js +11 -2
- package/dist/db/workflow-queries.js.map +1 -1
- package/dist/layout/component-refs.d.ts +465 -0
- package/dist/layout/component-refs.d.ts.map +1 -0
- package/dist/layout/component-refs.js +848 -0
- package/dist/layout/component-refs.js.map +1 -0
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +1 -0
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/module-permissions.d.ts +49 -1
- package/dist/layout/module-permissions.d.ts.map +1 -1
- package/dist/layout/module-permissions.js +117 -6
- package/dist/layout/module-permissions.js.map +1 -1
- package/dist/layout/renderer.d.ts +51 -4
- package/dist/layout/renderer.d.ts.map +1 -1
- package/dist/layout/renderer.js +36 -38
- package/dist/layout/renderer.js.map +1 -1
- package/dist/layout/types.d.ts +42 -0
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/layout/types.js +54 -11
- package/dist/layout/types.js.map +1 -1
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/components.d.ts +150 -0
- package/dist/schema/components.d.ts.map +1 -1
- package/dist/schema/components.js +50 -4
- package/dist/schema/components.js.map +1 -1
- package/dist/versioning/scheduler.d.ts +8 -0
- package/dist/versioning/scheduler.d.ts.map +1 -1
- package/dist/versioning/scheduler.js +21 -7
- package/dist/versioning/scheduler.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { LayoutDocument } from '../../layout/types.js';
|
|
3
|
+
import type { EditorAction } from './editor-state.js';
|
|
4
|
+
import { type ComponentForkSaveMode } from './editor-dialog.js';
|
|
5
|
+
import type { ComponentLibrary } from './use-component-library.js';
|
|
6
|
+
/**
|
|
7
|
+
* use-component-fork.tsx
|
|
8
|
+
* The save fork, driven (kywi-cms#147 T4). One implementation for both editor
|
|
9
|
+
* shells, as with the connect flow.
|
|
10
|
+
*
|
|
11
|
+
* WHAT IT DOES TO A SAVE. Every save path in both shells goes through
|
|
12
|
+
* {@link ComponentFork.guardSave} instead of handing `state.layout` straight to
|
|
13
|
+
* its callback. With no edited linked instances that is a pass-through — the
|
|
14
|
+
* document goes out unchanged, and nothing about saving has changed. With one or
|
|
15
|
+
* more, the save is INTERRUPTED: the owner answers per component, the answers are
|
|
16
|
+
* applied to the document, the definitions that need writing are written, and
|
|
17
|
+
* only then does the save proceed — with the resolved document, so the page that
|
|
18
|
+
* lands on the server is one whose linked instances are all coherent again.
|
|
19
|
+
*
|
|
20
|
+
* ORDERING, AND THE PARTIAL STATE IT ADMITS TO. Component definitions are PUT
|
|
21
|
+
* first, the page second. That is the safer order of the two: a definition write
|
|
22
|
+
* is idempotent and self-contained (it makes every instance render the edit, on
|
|
23
|
+
* this page too — resolution is live), whereas a page write that landed before a
|
|
24
|
+
* failed definition write would leave the page storing a CLEAN instance of a
|
|
25
|
+
* definition that never received the edit, i.e. the edit deleted. The remaining
|
|
26
|
+
* failure — definitions written, page save failed — is real and is SAID rather
|
|
27
|
+
* than hidden: the message names it, nothing is marked saved, the document stays
|
|
28
|
+
* dirty, and retrying re-runs the same idempotent PUT and saves again.
|
|
29
|
+
*
|
|
30
|
+
* THE SECOND DOOR, AND WHY IT IS NOW SHUT (kywi-cms#147 T4 review, I1). The fork
|
|
31
|
+
* sits on the EDITOR's own save paths. The admin Layout tab used to also stage
|
|
32
|
+
* every edit into the surrounding content form (`onChange` → `handleChange`), so a
|
|
33
|
+
* page-level form Submit persisted a layout the fork had never seen, and switching
|
|
34
|
+
* tabs remounted the editor with that staged document as its new baseline — which
|
|
35
|
+
* made the staged state permanent rather than provisional. For a SECTION or
|
|
36
|
+
* CONTAINER instance the consequence was worse than a stray override: those resolve
|
|
37
|
+
* WHOLESALE, so the edit sat in a fallback copy the renderer never reads. It was
|
|
38
|
+
* persisted and never rendered, and the following editor session took it as the
|
|
39
|
+
* baseline, so nothing normalised it either.
|
|
40
|
+
*
|
|
41
|
+
* `content-form.tsx` therefore no longer stages the layout, and no longer sends it
|
|
42
|
+
* with the page form — the Layout tab's own Save is the layout's save path, and the
|
|
43
|
+
* fork is on it. The one exception is a page being CREATED, where there is no
|
|
44
|
+
* content row to PUT a layout against and the create payload is the only door
|
|
45
|
+
* there is; a first-save page's linked instances were all connected in this same
|
|
46
|
+
* session, so their definitions were written by the connect flow moments earlier.
|
|
47
|
+
* The front-of-site overlay — the primary editing surface — never had a second door.
|
|
48
|
+
*/
|
|
49
|
+
/** What the shell hands over: persist this document, report whether it landed. */
|
|
50
|
+
export type PerformSave = (layout: LayoutDocument) => boolean | void | Promise<boolean | void>;
|
|
51
|
+
export interface ComponentFork {
|
|
52
|
+
/** The open fork dialog, or null. Render anywhere — it portals to the body. */
|
|
53
|
+
dialog: React.ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Run a save, asking about edited linked instances first.
|
|
56
|
+
*
|
|
57
|
+
* `perform` must be the shell's own persist step and must REPORT the outcome
|
|
58
|
+
* the way `onSave` does (`false`/rejection = did not persist), or a failed page
|
|
59
|
+
* save after a successful definition write cannot be told apart from a clean
|
|
60
|
+
* one.
|
|
61
|
+
*
|
|
62
|
+
* `mode` is which button was pressed, and only changes what the dialog SAYS: a
|
|
63
|
+
* component write is immediate whichever one it was, which is exactly the thing
|
|
64
|
+
* "Save Draft" and "Schedule" imply is not happening.
|
|
65
|
+
*/
|
|
66
|
+
guardSave: (perform: PerformSave, mode?: ComponentForkSaveMode) => void;
|
|
67
|
+
}
|
|
68
|
+
export declare function useComponentFork({ layout, originalLayout, dispatch, library, inlineEditActive, inert, }: {
|
|
69
|
+
layout: LayoutDocument;
|
|
70
|
+
/** The last-saved document — the baseline an instance is "edited" against. */
|
|
71
|
+
originalLayout: LayoutDocument;
|
|
72
|
+
dispatch: React.Dispatch<EditorAction>;
|
|
73
|
+
library: ComponentLibrary;
|
|
74
|
+
/**
|
|
75
|
+
* True while the front-of-site overlay has a LIVE inline text edit open —
|
|
76
|
+
* i.e. `OverlayShell`'s own `textEdit.id !== null` (kywi-cms#147 T4 review
|
|
77
|
+
* round 2). The admin canvas never has one (module-overlay.tsx: "admin canvas
|
|
78
|
+
* never carries `inlineEdit`"), so it simply never passes this.
|
|
79
|
+
*
|
|
80
|
+
* Why the fallback-copy refresh below needs to know: an inline edit's
|
|
81
|
+
* keystrokes live in the DOM only until commit (blur/Enter), so
|
|
82
|
+
* `state.isDirty` — which the reducer's REFRESH_COMPONENT_COPIES case already
|
|
83
|
+
* refuses to touch — never sees them. A library load landing mid-session finds
|
|
84
|
+
* a document the reducer calls pristine and refreshes it anyway. Worse than an
|
|
85
|
+
* ordinary stale-copy overwrite: `rekeyResolvedSection` gives the refreshed
|
|
86
|
+
* copy the exact ids the placement already had, so React updates the bound
|
|
87
|
+
* element's text IN PLACE instead of remounting it — the #145 orphan recovery
|
|
88
|
+
* (`isOrphanedEdit` in module-overlay.tsx) exists for a bound element that
|
|
89
|
+
* DISCONNECTS, and this element never does, so recovery never fires. The
|
|
90
|
+
* result (pinned in `overlay-refresh-mid-inline-edit.test.tsx`) is a session
|
|
91
|
+
* that looks perfectly healthy — still `contenteditable`, no cancel, no error
|
|
92
|
+
* — while the refresh has silently overwritten whatever was typed with the
|
|
93
|
+
* definition's current value.
|
|
94
|
+
*/
|
|
95
|
+
inlineEditActive?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* THE DOCUMENT IS NOT A PAGE, so there is no fork (kywi-cms#147 T5 review I1,
|
|
98
|
+
* final review M2). Set only by `LayoutEditor`'s `canvasKind` — the admin
|
|
99
|
+
* `components.edit` definition canvas, and the standalone Layouts editor's
|
|
100
|
+
* blueprint canvas.
|
|
101
|
+
*
|
|
102
|
+
* WHY INERT AND NOT "CAREFUL". The fork's question is "does this edit belong to
|
|
103
|
+
* the component, or to this page only?" — and on neither canvas is there a "this
|
|
104
|
+
* page". On a definition canvas the answer is fixed before the question (a
|
|
105
|
+
* definition save IS the propagation) and every branch behind it is wrong:
|
|
106
|
+
* "update all" would PUT some OTHER component's definition (whatever the edited
|
|
107
|
+
* node happened to link to) and only then let the definition save be refused by
|
|
108
|
+
* the extract's nested-link check — a site-wide write nobody asked for, in place
|
|
109
|
+
* of the one they did — while "this page only" would detach a node inside a
|
|
110
|
+
* definition. On a blueprint canvas the blast radius the dialog would name is not
|
|
111
|
+
* even readable: blueprints are outside the instance index, so a linked node in
|
|
112
|
+
* one reports "0 instances across 0 pages" and every number in the fork's copy
|
|
113
|
+
* would be a lie.
|
|
114
|
+
*
|
|
115
|
+
* `guardSave` therefore passes straight through, no dialog is ever built, and the
|
|
116
|
+
* fallback-copy refresh is skipped too: refreshing rewrites a linked node's
|
|
117
|
+
* inline copy from the library, which inside a definition would be silently
|
|
118
|
+
* editing stored content the owner never touched, and inside a blueprint would
|
|
119
|
+
* rewrite a stencil nobody opened for that.
|
|
120
|
+
*/
|
|
121
|
+
inert?: boolean;
|
|
122
|
+
}): ComponentFork;
|
|
123
|
+
//# sourceMappingURL=use-component-fork.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-fork.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-fork.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,cAAc,EAA+C,MAAM,uBAAuB,CAAA;AAUxG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAUrD,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAIH,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,cAAc,KACnB,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;AAE7C,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,MAAM,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,qBAAqB,KAAK,IAAI,CAAA;CACxE;AA6BD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,EACP,gBAAwB,EACxB,KAAa,GACd,EAAE;IACD,MAAM,EAAE,cAAc,CAAA;IACtB,8EAA8E;IAC9E,cAAc,EAAE,cAAc,CAAA;IAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IACtC,OAAO,EAAE,gBAAgB,CAAA;IACzB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,GAAG,aAAa,CA+RhB"}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { collectComponentInstances, componentDetachSource, componentRefKey, refreshComponentCopies, } from '../../layout/component-refs.js';
|
|
5
|
+
import { adminFetch } from '../../host-client/index.js';
|
|
6
|
+
import { applyInstanceReplacements, buildUpdateAll, detachInstanceKeepingEdits, diffLinkedInstances, isBaselineStale, linkedInstanceContentEquals, } from './component-fork.js';
|
|
7
|
+
import { ComponentForkDialog, EditorDialog, } from './editor-dialog.js';
|
|
8
|
+
import { componentErrorMessage } from './use-component-connect.js';
|
|
9
|
+
// ── The hook ─────────────────────────────────────────────────────────────────
|
|
10
|
+
export function useComponentFork({ layout, originalLayout, dispatch, library, inlineEditActive = false, inert = false, }) {
|
|
11
|
+
const [session, setSession] = React.useState(null);
|
|
12
|
+
const [phase, setPhase] = React.useState({ kind: 'asking', index: 0 });
|
|
13
|
+
const [usage, setUsage] = React.useState({});
|
|
14
|
+
// ── Session baselines ──────────────────────────────────────────────────────
|
|
15
|
+
//
|
|
16
|
+
// A link created during this session has no stored self to be compared with,
|
|
17
|
+
// and connecting/placing already wrote the definition — so its baseline is what
|
|
18
|
+
// it looked like the moment the link appeared. Recorded here, first sight
|
|
19
|
+
// winning, because otherwise "save as component, then fix the typo you just
|
|
20
|
+
// spotted" would write the fix into a fallback copy the page never renders.
|
|
21
|
+
// Entries for placements that later reach the server are simply ignored (the
|
|
22
|
+
// stored document wins in `diffLinkedInstances`), so this never goes stale in a
|
|
23
|
+
// way that matters.
|
|
24
|
+
const sessionBaselines = React.useRef(new Map());
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const stored = new Set(collectComponentInstances(originalLayout).map(componentRefKey));
|
|
27
|
+
for (const instance of collectComponentInstances(layout)) {
|
|
28
|
+
const key = componentRefKey(instance);
|
|
29
|
+
if (stored.has(key) || sessionBaselines.current.has(key))
|
|
30
|
+
continue;
|
|
31
|
+
sessionBaselines.current.set(key, instance);
|
|
32
|
+
}
|
|
33
|
+
}, [layout, originalLayout]);
|
|
34
|
+
// ── Fallback-copy refresh (kywi-cms#147 T4 review, C1) ─────────────────────
|
|
35
|
+
//
|
|
36
|
+
// A linked section/container stores an inline copy of its definition and the
|
|
37
|
+
// editor edits THAT — so before anything can be edited, the copies are brought
|
|
38
|
+
// back in line with the definitions the library just loaded. Without it,
|
|
39
|
+
// update-all built the new definition out of a stale copy and reverted every
|
|
40
|
+
// newer edit of that component everywhere, silently, published pages included.
|
|
41
|
+
//
|
|
42
|
+
// Once per library SNAPSHOT: the components array is a fresh identity on load, on
|
|
43
|
+
// `add` (a component this session created) and on `update` (a definition this
|
|
44
|
+
// session wrote), and each of those genuinely changes what a copy should hold.
|
|
45
|
+
// The layout is a dependency because the effect reads it, not because a layout
|
|
46
|
+
// change should re-run this — the ref guard is what stops that, and it has to,
|
|
47
|
+
// since every edit changes the layout.
|
|
48
|
+
//
|
|
49
|
+
// The reducer refuses the dispatch on a document that is no longer pristine (see
|
|
50
|
+
// REFRESH_COMPONENT_COPIES), so an author already mid-edit when a slow library
|
|
51
|
+
// arrives keeps their work and is protected by `isBaselineStale` instead.
|
|
52
|
+
const refreshedForRef = React.useRef(null);
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
// A definition canvas has no placements to refresh — see `inert`.
|
|
55
|
+
if (inert)
|
|
56
|
+
return;
|
|
57
|
+
// Deliberately does NOT update `refreshedForRef` on this branch, so the
|
|
58
|
+
// effect retries against the SAME library snapshot once the session ends
|
|
59
|
+
// (`inlineEditActive` flipping back to false is itself a dependency below,
|
|
60
|
+
// which re-runs this effect) — see `inlineEditActive`'s doc for why landing
|
|
61
|
+
// mid-session is unsafe rather than merely late.
|
|
62
|
+
if (inlineEditActive)
|
|
63
|
+
return;
|
|
64
|
+
if (refreshedForRef.current === library.components)
|
|
65
|
+
return;
|
|
66
|
+
refreshedForRef.current = library.components;
|
|
67
|
+
const refreshed = refreshComponentCopies(layout, library.lookup);
|
|
68
|
+
if (refreshed === layout)
|
|
69
|
+
return;
|
|
70
|
+
dispatch({ type: 'REFRESH_COMPONENT_COPIES', layout: refreshed });
|
|
71
|
+
}, [library, layout, dispatch, inlineEditActive, inert]);
|
|
72
|
+
const close = React.useCallback(() => {
|
|
73
|
+
setSession(null);
|
|
74
|
+
setPhase({ kind: 'asking', index: 0 });
|
|
75
|
+
}, []);
|
|
76
|
+
// ── Opening ────────────────────────────────────────────────────────────────
|
|
77
|
+
const guardSave = React.useCallback((perform, mode = 'save') => {
|
|
78
|
+
// A definition save is the propagation; there is nothing to ask (see `inert`).
|
|
79
|
+
if (inert) {
|
|
80
|
+
void perform(layout);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// A save while the dialog is open would ask twice about the same edits.
|
|
84
|
+
if (session)
|
|
85
|
+
return;
|
|
86
|
+
const edits = diffLinkedInstances(originalLayout, layout, ref => sessionBaselines.current.get(componentRefKey(ref)));
|
|
87
|
+
if (edits.length === 0) {
|
|
88
|
+
void perform(layout);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const groups = groupEdits(edits, library);
|
|
92
|
+
setSession({ layout, groups, decisions: {}, perform, mode });
|
|
93
|
+
setPhase({ kind: 'asking', index: 0 });
|
|
94
|
+
// Blast radius, best-effort and in parallel: one usage read per component,
|
|
95
|
+
// fired now so the numbers are there by the time the owner reads them.
|
|
96
|
+
setUsage(Object.fromEntries(groups.map(g => [g.componentId, { state: 'pending' }])));
|
|
97
|
+
for (const group of groups) {
|
|
98
|
+
void fetchUsage(group.componentId).then(radius => {
|
|
99
|
+
setUsage(prev => ({ ...prev, [group.componentId]: radius }));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}, [session, layout, originalLayout, library, inert]);
|
|
103
|
+
// ── Executing ──────────────────────────────────────────────────────────────
|
|
104
|
+
const execute = React.useCallback(async (current) => {
|
|
105
|
+
setPhase({ kind: 'saving' });
|
|
106
|
+
const replacements = new Map();
|
|
107
|
+
const updated = [];
|
|
108
|
+
// Definitions this save has written, so the OTHER placements of the same
|
|
109
|
+
// component — the ones nobody edited — can be refreshed to it before the page
|
|
110
|
+
// is stored (kywi-cms#147 T4 review, C1). Without this, editing placement A,
|
|
111
|
+
// updating everywhere, then editing placement B and updating again rebuilt the
|
|
112
|
+
// definition from B's now-stale copy and destroyed A's edit inside one session.
|
|
113
|
+
const written = new Map();
|
|
114
|
+
for (const group of current.groups) {
|
|
115
|
+
const decision = current.decisions[group.componentId] ?? 'this-page-only';
|
|
116
|
+
if (decision === 'this-page-only') {
|
|
117
|
+
for (const edit of group.edits) {
|
|
118
|
+
replacements.set(componentRefKey(edit), detachInstanceKeepingEdits(edit, library.lookup(edit.componentId)));
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Every edit in the group agrees (that is what makes update-all
|
|
123
|
+
// available), so any of them defines the component; each placement still
|
|
124
|
+
// gets its own clean instance, since a clean module instance is built
|
|
125
|
+
// from the node it replaces.
|
|
126
|
+
const plans = group.edits.map(edit => buildUpdateAll(edit, {
|
|
127
|
+
source: library.lookup(edit.componentId),
|
|
128
|
+
resolveLink: library.lookup,
|
|
129
|
+
}));
|
|
130
|
+
const definition = plans[0]?.definition;
|
|
131
|
+
if (!definition || plans.some(plan => plan === null)) {
|
|
132
|
+
// Only reachable if the library changed under us between the question
|
|
133
|
+
// and the answer (`groupEdits` already withdrew update-all otherwise).
|
|
134
|
+
setPhase({
|
|
135
|
+
kind: 'failed',
|
|
136
|
+
message: `${alreadyUpdated(updated)}Could not work out what to save for ` +
|
|
137
|
+
`“${group.name ?? 'this component'}”, so the page was not saved.`,
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let res;
|
|
142
|
+
try {
|
|
143
|
+
res = await adminFetch(`/api/v1/components/${group.componentId}`, {
|
|
144
|
+
method: 'PUT',
|
|
145
|
+
body: JSON.stringify({ definition }),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
setPhase({
|
|
150
|
+
kind: 'failed',
|
|
151
|
+
message: `${alreadyUpdated(updated)}Could not reach the server, so “${group.name ?? 'this component'}” was not updated and the page was not saved.`,
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
setPhase({
|
|
157
|
+
kind: 'failed',
|
|
158
|
+
message: `${alreadyUpdated(updated)}“${group.name ?? 'This component'}” was not updated: ${await componentErrorMessage(res)} The page was not saved.`,
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
updated.push(group.name ?? 'the component');
|
|
163
|
+
group.edits.forEach((edit, index) => {
|
|
164
|
+
replacements.set(componentRefKey(edit), plans[index].cleanInstance);
|
|
165
|
+
});
|
|
166
|
+
// The library is the editor's copy of what the server now holds — every
|
|
167
|
+
// later read of it (the rail's values, the canvas preview, the next
|
|
168
|
+
// staleness check) must see the definition this save just wrote.
|
|
169
|
+
const row = library.byId(group.componentId);
|
|
170
|
+
if (row) {
|
|
171
|
+
written.set(group.componentId, componentDetachSource({ ...row, definition }));
|
|
172
|
+
}
|
|
173
|
+
library.update(group.componentId, definition);
|
|
174
|
+
}
|
|
175
|
+
// One document, one history entry, handed to the reducer BY REFERENCE so
|
|
176
|
+
// the shell's MARK_SAVED can recognise it (see the action's doc).
|
|
177
|
+
//
|
|
178
|
+
// Two passes, in this order: the answers first (each edited placement becomes
|
|
179
|
+
// a clean instance of the new definition), then the copy refresh for the
|
|
180
|
+
// placements nobody edited. The refresh is a no-op on the ones just replaced —
|
|
181
|
+
// `buildUpdateAll` builds `cleanInstance` by applying the new definition to the
|
|
182
|
+
// placement, which is precisely what refreshing would compute.
|
|
183
|
+
const answered = applyInstanceReplacements(current.layout, replacements);
|
|
184
|
+
const resolved = written.size > 0
|
|
185
|
+
? refreshComponentCopies(answered, id => written.get(id) ?? null)
|
|
186
|
+
: answered;
|
|
187
|
+
dispatch({ type: 'APPLY_COMPONENT_FORK', layout: resolved });
|
|
188
|
+
let ok;
|
|
189
|
+
try {
|
|
190
|
+
ok = await Promise.resolve(current.perform(resolved));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
ok = false;
|
|
194
|
+
}
|
|
195
|
+
if (ok === false) {
|
|
196
|
+
setPhase({
|
|
197
|
+
kind: 'failed',
|
|
198
|
+
message: `${alreadyUpdated(updated)}This page could not be saved, so it still has unsaved changes. ` +
|
|
199
|
+
'Try again — updating a component twice changes nothing.',
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
close();
|
|
204
|
+
}, [library, dispatch, close]);
|
|
205
|
+
const decide = React.useCallback((decision) => {
|
|
206
|
+
if (!session)
|
|
207
|
+
return;
|
|
208
|
+
if (phase.kind !== 'asking')
|
|
209
|
+
return;
|
|
210
|
+
const group = session.groups[phase.index];
|
|
211
|
+
if (!group)
|
|
212
|
+
return;
|
|
213
|
+
const next = {
|
|
214
|
+
...session,
|
|
215
|
+
decisions: { ...session.decisions, [group.componentId]: decision },
|
|
216
|
+
};
|
|
217
|
+
setSession(next);
|
|
218
|
+
if (phase.index + 1 < next.groups.length) {
|
|
219
|
+
setPhase({ kind: 'asking', index: phase.index + 1 });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
void execute(next);
|
|
223
|
+
}, [session, phase, execute]);
|
|
224
|
+
const retry = React.useCallback(() => {
|
|
225
|
+
if (session)
|
|
226
|
+
void execute(session);
|
|
227
|
+
}, [session, execute]);
|
|
228
|
+
// ── The dialog ─────────────────────────────────────────────────────────────
|
|
229
|
+
const dialog = React.useMemo(() => {
|
|
230
|
+
if (!session)
|
|
231
|
+
return null;
|
|
232
|
+
// A failure replaces the question rather than decorating it: the answers have
|
|
233
|
+
// already been given, and re-offering "Save to this page only" here would
|
|
234
|
+
// re-run whatever was decided instead of what the label says. One honest
|
|
235
|
+
// action — try the same thing again — plus a way to walk away.
|
|
236
|
+
if (phase.kind === 'failed') {
|
|
237
|
+
return (_jsx(EditorDialog, { title: "Could not finish saving", error: phase.message, cancelLabel: "Close", actions: [{ label: 'Try again', action: 'fork-retry', onClick: retry }], onCancel: close }));
|
|
238
|
+
}
|
|
239
|
+
// While saving, the last question stays on screen with its actions disabled —
|
|
240
|
+
// the owner keeps the context of what they just decided.
|
|
241
|
+
const index = phase.kind === 'asking' ? phase.index : session.groups.length - 1;
|
|
242
|
+
const group = session.groups[Math.min(index, session.groups.length - 1)];
|
|
243
|
+
if (!group)
|
|
244
|
+
return null;
|
|
245
|
+
return (_jsx(ComponentForkDialog, { componentName: group.name, blastRadius: usage[group.componentId] ?? { state: 'pending' }, saveMode: session.mode, step: index + 1, stepCount: session.groups.length, updateAllUnavailable: group.updateAllUnavailable, busy: phase.kind === 'saving', onUpdateAll: () => decide('update-all'), onThisPageOnly: () => decide('this-page-only'),
|
|
246
|
+
// Cancel/Escape aborts the WHOLE save: nothing is written, the document
|
|
247
|
+
// keeps every edit, and the page stays dirty. A half-applied fork would
|
|
248
|
+
// be worse than no save at all.
|
|
249
|
+
onCancel: close }));
|
|
250
|
+
}, [session, phase, usage, decide, retry, close]);
|
|
251
|
+
return React.useMemo(() => ({ dialog, guardSave }), [dialog, guardSave]);
|
|
252
|
+
}
|
|
253
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
254
|
+
function alreadyUpdated(updated) {
|
|
255
|
+
if (updated.length === 0)
|
|
256
|
+
return '';
|
|
257
|
+
const names = updated.map(name => `“${name}”`).join(', ');
|
|
258
|
+
return `${names} ${updated.length === 1 ? 'was' : 'were'} already updated everywhere. `;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Group the edits by component — one question per component, not per node.
|
|
262
|
+
*
|
|
263
|
+
* MULTIPLE COMPONENTS IN ONE SAVE are asked about one after another ("Component 1
|
|
264
|
+
* of 2"), because the answers are genuinely independent: updating the shared
|
|
265
|
+
* footer everywhere and keeping a hero tweak local is a perfectly ordinary pair
|
|
266
|
+
* of intentions, and a single combined dialog would either force one answer for
|
|
267
|
+
* both or turn into a form.
|
|
268
|
+
*
|
|
269
|
+
* TWO PLACEMENTS OF ONE COMPONENT edited DIFFERENTLY on the same page cannot both
|
|
270
|
+
* become the definition. Rather than picking one silently, "update all" is
|
|
271
|
+
* withdrawn with the reason; "this page only" still works and detaches both.
|
|
272
|
+
* Identical edits to both placements are fine and stay offered.
|
|
273
|
+
*
|
|
274
|
+
* A STALE COPY withdraws it too (kywi-cms#147 T4 review, C1). If what this page
|
|
275
|
+
* stored as the component's content is not what the component now holds, the
|
|
276
|
+
* definition built from it would revert whatever changed in between — everywhere,
|
|
277
|
+
* published pages included. `isBaselineStale` decides; the copies are normally
|
|
278
|
+
* refreshed long before this point, so reaching it means the library never loaded
|
|
279
|
+
* or arrived too late to be applied safely.
|
|
280
|
+
*/
|
|
281
|
+
function groupEdits(edits, library) {
|
|
282
|
+
const groups = new Map();
|
|
283
|
+
for (const edit of edits) {
|
|
284
|
+
const existing = groups.get(edit.componentId);
|
|
285
|
+
if (existing) {
|
|
286
|
+
existing.edits.push(edit);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
groups.set(edit.componentId, {
|
|
290
|
+
componentId: edit.componentId,
|
|
291
|
+
name: library.nameOf(edit.componentId),
|
|
292
|
+
edits: [edit],
|
|
293
|
+
updateAllUnavailable: null,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
for (const group of groups.values()) {
|
|
297
|
+
const first = group.edits[0];
|
|
298
|
+
const divergent = group.edits.some(edit => !linkedInstanceContentEquals(first.instance, edit.instance));
|
|
299
|
+
if (divergent) {
|
|
300
|
+
group.updateAllUnavailable =
|
|
301
|
+
`${group.edits.length} instances of this component on this page were edited differently, ` +
|
|
302
|
+
'so they cannot all become the component. Save them to this page only, or undo one of the edits.';
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const source = library.lookup(group.componentId);
|
|
306
|
+
const buildable = group.edits.every(edit => buildUpdateAll(edit, { source, resolveLink: library.lookup }) !== null);
|
|
307
|
+
if (!buildable) {
|
|
308
|
+
group.updateAllUnavailable =
|
|
309
|
+
'This component’s current content could not be loaded, so the editor cannot tell which of ' +
|
|
310
|
+
'its settings you changed. Save to this page only, or reload the page and try again.';
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
// The belt on C1: a definition built out of an out-of-date copy would revert
|
|
314
|
+
// whatever changed in between, everywhere.
|
|
315
|
+
if (group.edits.some(edit => isBaselineStale(edit, source))) {
|
|
316
|
+
group.updateAllUnavailable =
|
|
317
|
+
'This page’s copy of the component is out of date, so saving it to every instance would ' +
|
|
318
|
+
'undo newer changes. Save to this page only, then re-edit the component from an ' +
|
|
319
|
+
'up-to-date page.';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [...groups.values()];
|
|
323
|
+
}
|
|
324
|
+
/** Where a component is placed — best-effort; a failure degrades the wording. */
|
|
325
|
+
async function fetchUsage(componentId) {
|
|
326
|
+
try {
|
|
327
|
+
const res = await adminFetch(`/api/v1/components/${componentId}/usage`);
|
|
328
|
+
if (!res.ok)
|
|
329
|
+
return { state: 'unknown' };
|
|
330
|
+
const body = (await res.json());
|
|
331
|
+
const data = body.data;
|
|
332
|
+
if (!data || typeof data.instances !== 'number' || typeof data.pages !== 'number') {
|
|
333
|
+
return { state: 'unknown' };
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
state: 'known',
|
|
337
|
+
instances: data.instances,
|
|
338
|
+
pages: data.pages,
|
|
339
|
+
trashedInstances: typeof data.trashedInstances === 'number' ? data.trashedInstances : 0,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return { state: 'unknown' };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=use-component-fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-fork.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-fork.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AACZ,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,GAGvB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAEvD,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,0BAA0B,EAC1B,mBAAmB,EACnB,eAAe,EACf,2BAA2B,GAE5B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,mBAAmB,EACnB,YAAY,GAGb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAiGlE,gFAAgF;AAEhF,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,EACP,gBAAgB,GAAG,KAAK,EACxB,KAAK,GAAG,KAAK,GAuDd;IACC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAqB,IAAI,CAAC,CAAA;IACtE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAA2C,EAAE,CAAC,CAAA;IAEtF,8EAA8E;IAC9E,EAAE;IACF,6EAA6E;IAC7E,gFAAgF;IAChF,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,6EAA6E;IAC7E,gFAAgF;IAChF,oBAAoB;IACpB,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,EAA6B,CAAC,CAAA;IAC3E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,yBAAyB,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAA;QACtF,KAAK,MAAM,QAAQ,IAAI,yBAAyB,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;YACrC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAQ;YAClE,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;IAE5B,8EAA8E;IAC9E,EAAE;IACF,6EAA6E;IAC7E,+EAA+E;IAC/E,yEAAyE;IACzE,6EAA6E;IAC7E,+EAA+E;IAC/E,EAAE;IACF,kFAAkF;IAClF,8EAA8E;IAC9E,+EAA+E;IAC/E,+EAA+E;IAC/E,+EAA+E;IAC/E,uCAAuC;IACvC,EAAE;IACF,iFAAiF;IACjF,+EAA+E;IAC/E,0EAA0E;IAC1E,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAU,IAAI,CAAC,CAAA;IACnD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,kEAAkE;QAClE,IAAI,KAAK;YAAE,OAAM;QACjB,wEAAwE;QACxE,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,iDAAiD;QACjD,IAAI,gBAAgB;YAAE,OAAM;QAC5B,IAAI,eAAe,CAAC,OAAO,KAAK,OAAO,CAAC,UAAU;YAAE,OAAM;QAC1D,eAAe,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAA;QAC5C,MAAM,SAAS,GAAG,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QAChE,IAAI,SAAS,KAAK,MAAM;YAAE,OAAM;QAChC,QAAQ,CAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IACnE,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC,CAAA;IAExD,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACnC,UAAU,CAAC,IAAI,CAAC,CAAA;QAChB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IACxC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,8EAA8E;IAE9E,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CACjC,CAAC,OAAoB,EAAE,OAA8B,MAAM,EAAE,EAAE;QAC7D,+EAA+E;QAC/E,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;YACpB,OAAM;QACR,CAAC;QACD,wEAAwE;QACxE,IAAI,OAAO;YAAE,OAAM;QACnB,MAAM,KAAK,GAAG,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAC9D,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CACnD,CAAA;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;YACpB,OAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QACzC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;QAEtC,2EAA2E;QAC3E,uEAAuE;QACvE,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAW,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7F,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;gBAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;YAC9D,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,EACD,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,CAAC,CAClD,CAAA;IAED,8EAA8E;IAE9E,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAC/B,KAAK,EAAE,OAAoB,EAAE,EAAE;QAC7B,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC5B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAyD,CAAA;QACrF,MAAM,OAAO,GAAa,EAAE,CAAA;QAC5B,yEAAyE;QACzE,8EAA8E;QAC9E,6EAA6E;QAC7E,+EAA+E;QAC/E,gFAAgF;QAChF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAA;QAExD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAA;YACzE,IAAI,QAAQ,KAAK,gBAAgB,EAAE,CAAC;gBAClC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC/B,YAAY,CAAC,GAAG,CACd,eAAe,CAAC,IAAI,CAAC,EACrB,0BAA0B,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CACnE,CAAA;gBACH,CAAC;gBACD,SAAQ;YACV,CAAC;YAED,gEAAgE;YAChE,yEAAyE;YACzE,sEAAsE;YACtE,6BAA6B;YAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CACnC,cAAc,CAAC,IAAI,EAAE;gBACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBACxC,WAAW,EAAE,OAAO,CAAC,MAAM;aAC5B,CAAC,CACH,CAAA;YACD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAA;YACvC,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACrD,sEAAsE;gBACtE,uEAAuE;gBACvE,QAAQ,CAAC;oBACP,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,GAAG,cAAc,CAAC,OAAO,CAAC,sCAAsC;wBAChE,IAAI,KAAK,CAAC,IAAI,IAAI,gBAAgB,+BAA+B;iBACpE,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YAED,IAAI,GAAa,CAAA;YACjB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,UAAU,CAAC,sBAAsB,KAAK,CAAC,WAAW,EAAE,EAAE;oBAChE,MAAM,EAAE,KAAK;oBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC;iBACrC,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC;oBACP,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,mCAAmC,KAAK,CAAC,IAAI,IAAI,gBAAgB,+CAA+C;iBACpJ,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,QAAQ,CAAC;oBACP,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,gBAAgB,sBAAsB,MAAM,qBAAqB,CAAC,GAAG,CAAC,0BAA0B;iBACtJ,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,eAAe,CAAC,CAAA;YAC3C,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAClC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,KAAK,CAAE,CAAC,aAAa,CAAC,CAAA;YACtE,CAAC,CAAC,CAAA;YACF,wEAAwE;YACxE,oEAAoE;YACpE,iEAAiE;YACjE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;YAC3C,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;YAC/E,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAC/C,CAAC;QAED,yEAAyE;QACzE,kEAAkE;QAClE,EAAE;QACF,8EAA8E;QAC9E,yEAAyE;QACzE,+EAA+E;QAC/E,gFAAgF;QAChF,+DAA+D;QAC/D,MAAM,QAAQ,GAAG,yBAAyB,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC;YAC/B,CAAC,CAAC,sBAAsB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;YACjE,CAAC,CAAC,QAAQ,CAAA;QACZ,QAAQ,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QAE5D,IAAI,EAAkB,CAAA;QACtB,IAAI,CAAC;YACH,EAAE,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,EAAE,GAAG,KAAK,CAAA;QACZ,CAAC;QACD,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;YACjB,QAAQ,CAAC;gBACP,IAAI,EAAE,QAAQ;gBACd,OAAO,EACL,GAAG,cAAc,CAAC,OAAO,CAAC,iEAAiE;oBAC3F,yDAAyD;aAC5D,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QACD,KAAK,EAAE,CAAA;IACT,CAAC,EACD,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAC3B,CAAA;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAC9B,CAAC,QAAsB,EAAE,EAAE;QACzB,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAM;QACnC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACzC,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,MAAM,IAAI,GAAgB;YACxB,GAAG,OAAO;YACV,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE;SACnE,CAAA;QACD,UAAU,CAAC,IAAI,CAAC,CAAA;QAChB,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACzC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;YACpD,OAAM;QACR,CAAC;QACD,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAC1B,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACnC,IAAI,OAAO;YAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACpC,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAEtB,8EAA8E;IAE9E,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QAChC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAEzB,8EAA8E;QAC9E,0EAA0E;QAC1E,yEAAyE;QACzE,+DAA+D;QAC/D,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,CACL,KAAC,YAAY,IACX,KAAK,EAAC,yBAAyB,EAC/B,KAAK,EAAE,KAAK,CAAC,OAAO,EACpB,WAAW,EAAC,OAAO,EACnB,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EACvE,QAAQ,EAAE,KAAK,GACf,CACH,CAAA;QACH,CAAC;QAED,8EAA8E;QAC9E,yDAAyD;QACzD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;QACxE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACvB,OAAO,CACL,KAAC,mBAAmB,IAClB,aAAa,EAAE,KAAK,CAAC,IAAI,EACzB,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAC7D,QAAQ,EAAE,OAAO,CAAC,IAAI,EACtB,IAAI,EAAE,KAAK,GAAG,CAAC,EACf,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAChC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB,EAChD,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,EACvC,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;YAC9C,wEAAwE;YACxE,wEAAwE;YACxE,gCAAgC;YAChC,QAAQ,EAAE,KAAK,GACf,CACH,CAAA;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;IAEjD,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;AAC1E,CAAC;AAED,gFAAgF;AAEhF,SAAS,cAAc,CAAC,OAAiB;IACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzD,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,+BAA+B,CAAA;AACzF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,UAAU,CAAC,KAA2B,EAAE,OAAyB;IACxE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACzB,SAAQ;QACV,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE;YAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YACtC,KAAK,EAAE,CAAC,IAAI,CAAC;YACb,oBAAoB,EAAE,IAAI;SAC3B,CAAC,CAAA;IACJ,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAA;QAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAChC,IAAI,CAAC,EAAE,CAAC,CAAC,2BAA2B,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CACpE,CAAA;QACD,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,oBAAoB;gBACxB,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,qEAAqE;oBAC1F,iGAAiG,CAAA;YACnG,SAAQ;QACV,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CACjC,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAC/E,CAAA;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,KAAK,CAAC,oBAAoB;gBACxB,2FAA2F;oBAC3F,qFAAqF,CAAA;YACvF,SAAQ;QACV,CAAC;QACD,6EAA6E;QAC7E,2CAA2C;QAC3C,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,oBAAoB;gBACxB,yFAAyF;oBACzF,iFAAiF;oBACjF,kBAAkB,CAAA;QACtB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AAC7B,CAAC;AAED,iFAAiF;AACjF,KAAK,UAAU,UAAU,CAAC,WAAmB;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,sBAAsB,WAAW,QAAQ,CAAC,CAAA;QACvE,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QACxC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAE7B,CAAA;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAClF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QAC7B,CAAC;QACD,OAAO;YACL,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,gBAAgB,EAAE,OAAO,IAAI,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;SACxF,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ComponentPlacementLookup } from '../../layout/component-refs.js';
|
|
2
|
+
import type { ModuleNode } from '../../layout/types.js';
|
|
3
|
+
import type { ComponentOption } from './component-picker-field.js';
|
|
4
|
+
/**
|
|
5
|
+
* use-component-library.ts
|
|
6
|
+
* The saved components an editor shell has loaded, and the projections both
|
|
7
|
+
* shells need from them (kywi-cms#147).
|
|
8
|
+
*
|
|
9
|
+
* WHY ONE HOOK FOR BOTH SHELLS. The admin Layout tab fetched module components
|
|
10
|
+
* and section components in two separate effects and kept them in two states; the
|
|
11
|
+
* front-of-site overlay fetched neither, which is why "Save as component" and the
|
|
12
|
+
* linked badge existed only in the admin editor even though the overlay is the
|
|
13
|
+
* PRIMARY editing surface (kywi-cms#94). One hook, one request, three types.
|
|
14
|
+
*
|
|
15
|
+
* The request is deliberately unfiltered (`GET /components`, no `?type=`): a
|
|
16
|
+
* document can hold links of all three kinds at once, every one of them needs a
|
|
17
|
+
* name for its pill and a definition for detach, and one round trip is cheaper
|
|
18
|
+
* than three. Best-effort throughout — a failed load leaves the library empty,
|
|
19
|
+
* which degrades pills to the generic "Component" and leaves save-as-component
|
|
20
|
+
* fully working (it only ever POSTs).
|
|
21
|
+
*/
|
|
22
|
+
/** A saved component as `GET /api/v1/components` returns it. */
|
|
23
|
+
export interface EditorComponent {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
moduleType: string | null;
|
|
28
|
+
definition: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ComponentLibrary {
|
|
31
|
+
/** Every loaded component, all types. */
|
|
32
|
+
components: EditorComponent[];
|
|
33
|
+
byId: (componentId: string) => EditorComponent | null;
|
|
34
|
+
/** Display name for a linked instance's pill, or null when unknown. */
|
|
35
|
+
nameOf: (componentId: string) => string | null;
|
|
36
|
+
/** Picker options for one component type. */
|
|
37
|
+
optionsFor: (type: 'module' | 'section' | 'variantContainer') => ComponentOption[];
|
|
38
|
+
/**
|
|
39
|
+
* The canonical lookup materialisation reads (`componentDetachSource`), used by
|
|
40
|
+
* detach, by save-as-component's nested-link flattening, and by anything else
|
|
41
|
+
* that has to answer "what does this link render".
|
|
42
|
+
*/
|
|
43
|
+
lookup: ComponentPlacementLookup;
|
|
44
|
+
/** Module-component projection for the canvas preview (kywi-cms#46). */
|
|
45
|
+
resolveModule: (componentId: string) => {
|
|
46
|
+
props: Record<string, unknown>;
|
|
47
|
+
style: Record<string, string>;
|
|
48
|
+
moduleType: string;
|
|
49
|
+
} | null;
|
|
50
|
+
/**
|
|
51
|
+
* What a module node RENDERS: its own props/style for an ordinary node, and for
|
|
52
|
+
* a linked instance the definition read through the canonical placement rule
|
|
53
|
+
* (`resolveModulePlacement` — definition alone for a clean instance, definition
|
|
54
|
+
* then node for a legacy override one).
|
|
55
|
+
*
|
|
56
|
+
* The props rail needs exactly this (kywi-cms#147 T4): a clean instance carries
|
|
57
|
+
* no props of its own, so fields fed from `node.props` would show an empty form
|
|
58
|
+
* for a module full of content, and the first keystroke would look like it
|
|
59
|
+
* cleared everything. Same projection as the canvas preview and the public
|
|
60
|
+
* renderer, so what the rail shows is what the visitor sees.
|
|
61
|
+
*/
|
|
62
|
+
renderedModule: (node: Pick<ModuleNode, 'props' | 'style' | 'componentId'>) => {
|
|
63
|
+
props: Record<string, unknown>;
|
|
64
|
+
style: Record<string, string>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Which module type's registry entry describes a node's editable FIELDS, or null
|
|
68
|
+
* when nothing honest can be shown (kywi-cms#147 T4 review, I2).
|
|
69
|
+
*
|
|
70
|
+
* For an ordinary node that is its own `type`. For a linked instance it is the
|
|
71
|
+
* COMPONENT's `moduleType`, because that is what the placement renders as
|
|
72
|
+
* (`materializeModuleNode` retypes the node) — and because a component placed
|
|
73
|
+
* from the palette picker is a `component` placeholder node, whose registry entry
|
|
74
|
+
* declares "Component Slug" and "Component ID" props. Those are link plumbing,
|
|
75
|
+
* not content: seeded from the resolved definition they read empty, and typing
|
|
76
|
+
* into "Component ID" writes a prop the fork deliberately strips.
|
|
77
|
+
*
|
|
78
|
+
* Null for a linked placeholder whose component does not resolve — there is no
|
|
79
|
+
* module type, so there are no fields, and inventing the placeholder's own two is
|
|
80
|
+
* how the rail came to show a form nobody could use.
|
|
81
|
+
*/
|
|
82
|
+
fieldModuleType: (node: Pick<ModuleNode, 'type' | 'componentId'>) => string | null;
|
|
83
|
+
/** Adopt a component the shell just created, so its name is available at once. */
|
|
84
|
+
add: (component: EditorComponent) => void;
|
|
85
|
+
/**
|
|
86
|
+
* Adopt a definition the shell just wrote (the save fork's `PUT`), so everything
|
|
87
|
+
* reading the library — the rail's field values, the canvas preview, the copy
|
|
88
|
+
* refresh, and the next fork's staleness check — sees the new content rather than
|
|
89
|
+
* the one the page loaded with (kywi-cms#147 T4 review, C1).
|
|
90
|
+
*/
|
|
91
|
+
update: (componentId: string, definition: unknown) => void;
|
|
92
|
+
}
|
|
93
|
+
export declare function useComponentLibrary(): ComponentLibrary;
|
|
94
|
+
//# sourceMappingURL=use-component-library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-library.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-library.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAElE;;;;;;;;;;;;;;;;;GAiBG;AAEH,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,eAAe,GAAG,IAAI,CAAA;IACrD,uEAAuE;IACvE,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAC9C,6CAA6C;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,kBAAkB,KAAK,eAAe,EAAE,CAAA;IAClF;;;;OAIG;IACH,MAAM,EAAE,wBAAwB,CAAA;IAChC,wEAAwE;IACxE,aAAa,EAAE,CACb,WAAW,EAAE,MAAM,KAChB;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACjG;;;;;;;;;;;OAWG;IACH,cAAc,EAAE,CACd,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC,KACtD;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACtE;;;;;;;;;;;;;;;OAeG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAClF,kFAAkF;IAClF,GAAG,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC;;;;;OAKG;IACH,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;CAC3D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,CAkFtD"}
|