@_linked/shape-ui 2.0.0 → 2.3.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.
Files changed (207) hide show
  1. package/lib/esm/columns.d.ts +58 -0
  2. package/lib/esm/columns.js +173 -0
  3. package/lib/esm/columns.js.map +1 -0
  4. package/lib/esm/components/AddInstanceForms.d.ts +38 -0
  5. package/lib/esm/components/AddInstanceForms.js +143 -0
  6. package/lib/esm/components/AddInstanceForms.js.map +1 -0
  7. package/lib/esm/components/AddInstanceForms.module.css +12 -0
  8. package/lib/esm/components/BatchEditDrawer.d.ts +13 -0
  9. package/lib/esm/components/BatchEditDrawer.js +122 -0
  10. package/lib/esm/components/BatchEditDrawer.js.map +1 -0
  11. package/lib/esm/components/BatchEditDrawer.module.css +193 -0
  12. package/lib/esm/components/CustomMultiSelect.d.ts +26 -0
  13. package/lib/esm/components/CustomMultiSelect.js +344 -0
  14. package/lib/esm/components/CustomMultiSelect.js.map +1 -0
  15. package/lib/esm/components/CustomMultiSelect.module.css +291 -0
  16. package/lib/esm/components/DynamicFormField.d.ts +21 -0
  17. package/lib/esm/components/DynamicFormField.js +149 -0
  18. package/lib/esm/components/DynamicFormField.js.map +1 -0
  19. package/lib/esm/components/DynamicFormField.module.css +169 -0
  20. package/lib/esm/components/EditInstanceForms.d.ts +15 -0
  21. package/lib/esm/components/EditInstanceForms.js +118 -0
  22. package/lib/esm/components/EditInstanceForms.js.map +1 -0
  23. package/lib/esm/components/FilterBadge.d.ts +20 -0
  24. package/lib/esm/components/FilterBadge.js +26 -0
  25. package/lib/esm/components/FilterBadge.js.map +1 -0
  26. package/lib/esm/components/FilterBadge.module.css +25 -0
  27. package/lib/esm/components/FormField.d.ts +18 -0
  28. package/lib/esm/components/FormField.js +9 -0
  29. package/lib/esm/components/FormField.js.map +1 -0
  30. package/lib/esm/components/FormField.module.css +57 -0
  31. package/lib/esm/components/InstanceDeletionDialog.d.ts +17 -0
  32. package/lib/esm/components/InstanceDeletionDialog.js +83 -0
  33. package/lib/esm/components/InstanceDeletionDialog.js.map +1 -0
  34. package/lib/esm/components/InstanceDeletionDialog.module.css +59 -0
  35. package/lib/esm/components/InstanceOverview.d.ts +43 -0
  36. package/lib/esm/components/InstanceOverview.js +236 -0
  37. package/lib/esm/components/InstanceOverview.js.map +1 -0
  38. package/lib/esm/components/InstanceOverview.module.css +35 -0
  39. package/lib/esm/components/InstanceView.d.ts +29 -0
  40. package/lib/esm/components/InstanceView.js +97 -0
  41. package/lib/esm/components/InstanceView.js.map +1 -0
  42. package/lib/esm/components/InstanceView.module.css +157 -0
  43. package/lib/esm/components/NodeBadge.d.ts +25 -0
  44. package/lib/esm/components/NodeBadge.js +29 -0
  45. package/lib/esm/components/NodeBadge.js.map +1 -0
  46. package/lib/esm/components/NodeValuesEditor.d.ts +15 -0
  47. package/lib/esm/components/NodeValuesEditor.js +29 -0
  48. package/lib/esm/components/NodeValuesEditor.js.map +1 -0
  49. package/lib/esm/components/NodeValuesEditor.module.css +4 -0
  50. package/lib/esm/components/ReactTable.d.ts +59 -0
  51. package/lib/esm/components/ReactTable.js +378 -0
  52. package/lib/esm/components/ReactTable.js.map +1 -0
  53. package/lib/esm/components/ReactTable.module.css +950 -0
  54. package/lib/esm/components/TextfieldEditor.d.ts +1 -2
  55. package/lib/esm/components/TextfieldEditor.js +2 -2
  56. package/lib/esm/components/TextfieldEditor.js.map +1 -1
  57. package/lib/esm/components/forms.module.css +96 -0
  58. package/lib/esm/host.d.ts +196 -0
  59. package/lib/esm/host.js +25 -0
  60. package/lib/esm/host.js.map +1 -0
  61. package/lib/esm/hostContext.d.ts +34 -0
  62. package/lib/esm/hostContext.js +95 -0
  63. package/lib/esm/hostContext.js.map +1 -0
  64. package/lib/esm/index.d.ts +60 -10
  65. package/lib/esm/index.js +60 -14
  66. package/lib/esm/index.js.map +1 -1
  67. package/lib/esm/read.d.ts +48 -0
  68. package/lib/esm/read.js +96 -0
  69. package/lib/esm/read.js.map +1 -0
  70. package/lib/esm/search.d.ts +32 -0
  71. package/lib/esm/search.js +87 -0
  72. package/lib/esm/search.js.map +1 -0
  73. package/lib/esm/shape/contracts.d.ts +58 -0
  74. package/lib/esm/shape/contracts.js +15 -0
  75. package/lib/esm/shape/contracts.js.map +1 -0
  76. package/lib/esm/shape/naming.d.ts +30 -0
  77. package/lib/esm/shape/naming.js +52 -0
  78. package/lib/esm/shape/naming.js.map +1 -0
  79. package/lib/esm/shape/nodeDisplay.d.ts +36 -0
  80. package/lib/esm/shape/nodeDisplay.js +117 -0
  81. package/lib/esm/shape/nodeDisplay.js.map +1 -0
  82. package/lib/esm/shape/propertyTypes.d.ts +12 -0
  83. package/lib/esm/shape/propertyTypes.js +60 -0
  84. package/lib/esm/shape/propertyTypes.js.map +1 -0
  85. package/lib/esm/shape/propertyVisibility.d.ts +56 -0
  86. package/lib/esm/shape/propertyVisibility.js +112 -0
  87. package/lib/esm/shape/propertyVisibility.js.map +1 -0
  88. package/lib/esm/shape/shapeDependencies.d.ts +41 -0
  89. package/lib/esm/shape/shapeDependencies.js +141 -0
  90. package/lib/esm/shape/shapeDependencies.js.map +1 -0
  91. package/lib/esm/shape/tableFilters.d.ts +35 -0
  92. package/lib/esm/shape/tableFilters.js +171 -0
  93. package/lib/esm/shape/tableFilters.js.map +1 -0
  94. package/lib/esm/shape/useFormDependencies.d.ts +38 -0
  95. package/lib/esm/shape/useFormDependencies.js +178 -0
  96. package/lib/esm/shape/useFormDependencies.js.map +1 -0
  97. package/lib/esm/shape/validation.d.ts +15 -0
  98. package/lib/esm/shape/validation.js +106 -0
  99. package/lib/esm/shape/validation.js.map +1 -0
  100. package/lib/esm/tokens.css +25 -0
  101. package/lib/esm/types.d.ts +22 -0
  102. package/lib/esm/types.js +1 -0
  103. package/lib/esm/types.js.map +1 -0
  104. package/lib/esm/write.d.ts +51 -0
  105. package/lib/esm/write.js +99 -0
  106. package/lib/esm/write.js.map +1 -0
  107. package/package.json +44 -18
  108. package/src/backend.ts +1 -0
  109. package/src/columns.regression.test.ts +143 -0
  110. package/src/columns.test.ts +200 -0
  111. package/src/columns.ts +228 -0
  112. package/src/components/AddInstanceForms.module.css +12 -0
  113. package/src/components/AddInstanceForms.tsx +321 -0
  114. package/src/components/AvatarEditor.tsx +34 -0
  115. package/src/components/BatchEditDrawer.module.css +193 -0
  116. package/src/components/BatchEditDrawer.tsx +295 -0
  117. package/src/components/CheckboxEditor.module.css +6 -0
  118. package/src/components/CheckboxEditor.tsx +86 -0
  119. package/src/components/CustomMultiSelect.module.css +291 -0
  120. package/src/components/CustomMultiSelect.tsx +527 -0
  121. package/src/components/DateEditor.tsx +50 -0
  122. package/src/components/DynamicFormField.module.css +169 -0
  123. package/src/components/DynamicFormField.tsx +357 -0
  124. package/src/components/EditInstanceForms.tsx +260 -0
  125. package/src/components/FilterBadge.module.css +25 -0
  126. package/src/components/FilterBadge.tsx +70 -0
  127. package/src/components/FormField.module.css +57 -0
  128. package/src/components/FormField.tsx +59 -0
  129. package/src/components/ImageValueEditor.module.css +3 -0
  130. package/src/components/ImageValueEditor.tsx +82 -0
  131. package/src/components/InstanceDeletionDialog.module.css +59 -0
  132. package/src/components/InstanceDeletionDialog.tsx +175 -0
  133. package/src/components/InstanceOverview.module.css +35 -0
  134. package/src/components/InstanceOverview.tsx +385 -0
  135. package/src/components/InstanceView.module.css +157 -0
  136. package/src/components/InstanceView.tsx +212 -0
  137. package/src/components/NodeBadge.tsx +60 -0
  138. package/src/components/NodeValuesEditor.module.css +4 -0
  139. package/src/components/NodeValuesEditor.tsx +64 -0
  140. package/src/components/RadioButtonEditor.module.css +22 -0
  141. package/src/components/RadioButtonEditor.tsx +96 -0
  142. package/src/components/ReactTable.module.css +950 -0
  143. package/src/components/ReactTable.tsx +1013 -0
  144. package/src/components/SelectEditor.tsx +85 -0
  145. package/src/components/ShapeTable.module.css +394 -0
  146. package/src/components/SwitchEditor.tsx +60 -0
  147. package/src/components/TextareaEditor.tsx +55 -0
  148. package/src/components/TextfieldEditor.tsx +67 -0
  149. package/src/components/ToggleEditor.tsx +63 -0
  150. package/src/components/forms.module.css +96 -0
  151. package/src/host.ts +226 -0
  152. package/src/hostContext.tsx +114 -0
  153. package/src/index.ts +93 -0
  154. package/src/package.ts +21 -0
  155. package/src/read.ts +148 -0
  156. package/src/search.test.ts +174 -0
  157. package/src/search.ts +108 -0
  158. package/src/shape/contracts.ts +66 -0
  159. package/src/shape/naming.ts +52 -0
  160. package/src/shape/nodeDisplay.ts +114 -0
  161. package/src/shape/propertyTypes.ts +86 -0
  162. package/src/shape/propertyVisibility.ts +180 -0
  163. package/src/shape/shapeDependencies.ts +183 -0
  164. package/src/shape/tableFilters.ts +223 -0
  165. package/src/shape/types.ts +1158 -0
  166. package/src/shape/useFormDependencies.ts +257 -0
  167. package/src/shape/validation.ts +121 -0
  168. package/src/tokens.css +25 -0
  169. package/src/types.ts +20 -0
  170. package/src/utils/helper.ts +35 -0
  171. package/src/write.test.ts +174 -0
  172. package/src/write.ts +114 -0
  173. package/.changeset/README.md +0 -20
  174. package/.changeset/config.json +0 -14
  175. package/.github/workflows/changeset-check.yml +0 -44
  176. package/.github/workflows/ci.yml +0 -43
  177. package/.github/workflows/publish.yml +0 -158
  178. package/CHANGELOG.md +0 -30
  179. package/lib/esm/components/AvatarEditor.d.ts +0 -20
  180. package/lib/esm/components/AvatarEditor.js +0 -7
  181. package/lib/esm/components/AvatarEditor.js.map +0 -1
  182. package/lib/esm/components/CheckboxEditor.d.ts +0 -21
  183. package/lib/esm/components/CheckboxEditor.js +0 -44
  184. package/lib/esm/components/CheckboxEditor.js.map +0 -1
  185. package/lib/esm/components/DateEditor.d.ts +0 -20
  186. package/lib/esm/components/DateEditor.js +0 -22
  187. package/lib/esm/components/DateEditor.js.map +0 -1
  188. package/lib/esm/components/RadioButtonEditor.d.ts +0 -23
  189. package/lib/esm/components/RadioButtonEditor.js +0 -41
  190. package/lib/esm/components/RadioButtonEditor.js.map +0 -1
  191. package/lib/esm/components/SelectEditor.d.ts +0 -21
  192. package/lib/esm/components/SelectEditor.js +0 -30
  193. package/lib/esm/components/SelectEditor.js.map +0 -1
  194. package/lib/esm/components/SwitchEditor.d.ts +0 -21
  195. package/lib/esm/components/SwitchEditor.js +0 -23
  196. package/lib/esm/components/SwitchEditor.js.map +0 -1
  197. package/lib/esm/components/TextareaEditor.d.ts +0 -23
  198. package/lib/esm/components/TextareaEditor.js +0 -15
  199. package/lib/esm/components/TextareaEditor.js.map +0 -1
  200. package/lib/esm/components/ToggleEditor.d.ts +0 -22
  201. package/lib/esm/components/ToggleEditor.js +0 -23
  202. package/lib/esm/components/ToggleEditor.js.map +0 -1
  203. package/lib/esm/ontologies/lincd-ui.d.ts +0 -6
  204. package/lib/esm/ontologies/lincd-ui.js +0 -18
  205. package/lib/esm/ontologies/lincd-ui.js.map +0 -1
  206. package/tsconfig-cjs.json +0 -8
  207. package/tsconfig-esm.json +0 -9
package/src/write.ts ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Creating and updating instances, through the Linked Query DSL.
3
+ *
4
+ * The counterpart of `search.ts`, and the last piece of the read/write pair that still
5
+ * went through a control-plane RPC. `Project.createShapeInstance(projectId, …)` needed a
6
+ * project id for three things, none of which require one on the client:
7
+ *
8
+ * - finding the shape → the host already resolved the catalog
9
+ * - minting the new IRI → `host.dataRoot`, a plain value the host supplies
10
+ * - recording a projection edit → a Create Now studio concern, not a write concern
11
+ *
12
+ * So the write names no project either, and `LinkedStorage` routes it exactly as it routes
13
+ * the read: `AppDataRouter` inside Create Now, the app's own dataset in a standalone or
14
+ * Capacitor build. That matters beyond tidiness — a read that routes one way and a write
15
+ * that routes another is how a form silently saves into the wrong dataset.
16
+ */
17
+
18
+ import {CreateBuilder} from '@_linked/core/queries/CreateBuilder';
19
+ import {UpdateBuilder} from '@_linked/core/queries/UpdateBuilder';
20
+ import type {NodeShapeWire} from '@_linked/core/shapes/nodeShapeWire';
21
+
22
+ /** A form's values, keyed by property label. */
23
+ export type InstanceValues = Record<string, unknown>;
24
+
25
+ /**
26
+ * Strip a value down to what the graph should hold.
27
+ *
28
+ * A relation field carries `{id, label}` — the label is there so a draft can show a name
29
+ * without re-reading the instance. It is display state, not a fact about the node, and
30
+ * writing it would put a second, immediately stale copy of the label in the graph.
31
+ */
32
+ function toGraphValue(value: unknown): unknown {
33
+ if (Array.isArray(value)) return value.map(toGraphValue);
34
+ if (value && typeof value === 'object' && 'id' in (value as Record<string, unknown>)) {
35
+ return {id: (value as {id: unknown}).id};
36
+ }
37
+ return value;
38
+ }
39
+
40
+ /** Labels the shape actually declares. Anything else is a stale form field. */
41
+ function knownLabels(shape: NodeShapeWire): Set<string> {
42
+ return new Set(
43
+ (shape.propertyShapes ?? []).map((p) => p.label).filter(Boolean) as string[],
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Mint an IRI for a new instance.
49
+ *
50
+ * `dataRoot` is the one value in this path the host has to supply: Create Now takes it
51
+ * from the project's config, a standalone app knows its own domain. Without it there is
52
+ * no namespace to mint into, so this throws rather than inventing one — a node created
53
+ * under a guessed IRI is worse than a failed create.
54
+ */
55
+ export function mintInstanceIri(shape: NodeShapeWire, dataRoot?: string): string {
56
+ if (!dataRoot) {
57
+ throw new Error(
58
+ 'Cannot create an instance without `dataRoot`: there is no namespace to mint the ' +
59
+ 'IRI into. Supply it from the host.',
60
+ );
61
+ }
62
+ const segment = (shape.label || 'instance').toLowerCase();
63
+ const base = dataRoot.replace(/\/+$/, '');
64
+ return `${base}/${segment}/${crypto.randomUUID()}`;
65
+ }
66
+
67
+ /**
68
+ * Create one instance and return its IRI.
69
+ *
70
+ * Empty values are dropped rather than written: on a create there is nothing to clear, and
71
+ * an empty literal is a real triple with an empty string in it.
72
+ */
73
+ export async function createInstanceWithDsl(
74
+ shape: NodeShapeWire,
75
+ values: InstanceValues,
76
+ options: {dataRoot?: string; id?: string} = {},
77
+ ): Promise<{id: string}> {
78
+ const known = knownLabels(shape);
79
+ const data: Record<string, unknown> = {};
80
+ for (const [label, value] of Object.entries(values)) {
81
+ if (value === undefined || value === null || value === '') continue;
82
+ if (!known.has(label)) continue;
83
+ data[label] = toGraphValue(value);
84
+ }
85
+
86
+ const id = options.id ?? mintInstanceIri(shape, options.dataRoot);
87
+ await (CreateBuilder.from(shape.id) as any).withId(id).set(data).exec();
88
+ return {id};
89
+ }
90
+
91
+ /**
92
+ * Update one instance.
93
+ *
94
+ * Empty becomes `null`, which is how the DSL clears a property — the opposite of create,
95
+ * where the same value is dropped. Both are right: clearing a field the viewer emptied is
96
+ * the whole point of an edit form, and there is nothing to clear on a node that does not
97
+ * exist yet.
98
+ *
99
+ * A payload with nothing recognisable in it is a no-op rather than an empty mutation.
100
+ */
101
+ export async function updateInstanceWithDsl(
102
+ shape: NodeShapeWire,
103
+ instanceId: string,
104
+ values: InstanceValues,
105
+ ): Promise<void> {
106
+ const known = knownLabels(shape);
107
+ const data: Record<string, unknown> = {};
108
+ for (const [label, value] of Object.entries(values)) {
109
+ if (!known.has(label)) continue;
110
+ data[label] = value === '' || value === undefined ? null : toGraphValue(value);
111
+ }
112
+ if (Object.keys(data).length === 0) return;
113
+ await (UpdateBuilder.from(shape.id) as any).for(instanceId).set(data).exec();
114
+ }
@@ -1,20 +0,0 @@
1
- # Changesets
2
-
3
- This package uses [changesets](https://github.com/changesets/changesets) for version management and publishing.
4
-
5
- ## Adding a changeset
6
-
7
- When making a change that should trigger a new release, run:
8
-
9
- ```bash
10
- npx changeset
11
- ```
12
-
13
- This generates a markdown file in `.changeset/` describing the change. Commit it with your PR.
14
-
15
- On merge to `main`, the publish workflow either:
16
-
17
- 1. Creates (or updates) a **Release PR** that bumps the version + updates `CHANGELOG.md`, or
18
- 2. If the Release PR is already merged, publishes the new version to npm.
19
-
20
- Changes that don't need a release (docs, CI config, tooling) can skip the changeset — the check is a warning, not a block.
@@ -1,14 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
- "changelog": [
4
- "@changesets/changelog-github",
5
- { "repo": "linked-cm/ui" }
6
- ],
7
- "commit": false,
8
- "fixed": [],
9
- "linked": [],
10
- "access": "public",
11
- "baseBranch": "main",
12
- "updateInternalDependencies": "patch",
13
- "ignore": []
14
- }
@@ -1,44 +0,0 @@
1
- name: Changeset Check
2
-
3
- on:
4
- pull_request:
5
- branches:
6
- - main
7
-
8
- jobs:
9
- changeset-check:
10
- name: Check for changeset
11
- # Skip the check on the automated Release PR.
12
- if: ${{ github.head_ref != 'changeset-release/main' }}
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
- with:
18
- fetch-depth: 0
19
-
20
- - name: Check for changeset files
21
- uses: actions/github-script@v7
22
- with:
23
- script: |
24
- const { data: files } = await github.rest.pulls.listFiles({
25
- owner: context.repo.owner,
26
- repo: context.repo.repo,
27
- pull_number: context.issue.number,
28
- });
29
-
30
- const hasChangeset = files.some(f =>
31
- f.filename.startsWith('.changeset/') &&
32
- f.filename.endsWith('.md') &&
33
- f.filename !== '.changeset/README.md'
34
- );
35
-
36
- if (!hasChangeset) {
37
- await github.rest.issues.createComment({
38
- owner: context.repo.owner,
39
- repo: context.repo.repo,
40
- issue_number: context.issue.number,
41
- body: "No changeset found. If this PR should trigger a release, run `npx changeset` and commit the generated file.\n\nIf this change doesn't need a release (docs, CI config, etc.), you can ignore this message."
42
- });
43
- core.warning('No changeset file found in this PR.');
44
- }
@@ -1,43 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- branches:
6
- - main
7
-
8
- jobs:
9
- build-and-test:
10
- name: Build & Test
11
- runs-on: ubuntu-latest
12
- steps:
13
- - name: Detect Release PR
14
- id: rpr
15
- run: |
16
- if [[ "${{ github.event.pull_request.head.ref }}" == "changeset-release/main" ]]; then
17
- echo "is_release_pr=true" >> $GITHUB_OUTPUT
18
- echo "Release PR from changesets/action detected — skipping build/test (auto-generated version+CHANGELOG only)."
19
- else
20
- echo "is_release_pr=false" >> $GITHUB_OUTPUT
21
- fi
22
-
23
- - name: Checkout
24
- if: steps.rpr.outputs.is_release_pr == 'false'
25
- uses: actions/checkout@v4
26
-
27
- - name: Setup Node.js
28
- if: steps.rpr.outputs.is_release_pr == 'false'
29
- uses: actions/setup-node@v4
30
- with:
31
- node-version: 22.16.0
32
-
33
- - name: Install dependencies
34
- if: steps.rpr.outputs.is_release_pr == 'false'
35
- run: npm ci --legacy-peer-deps
36
-
37
- - name: Build
38
- if: steps.rpr.outputs.is_release_pr == 'false'
39
- run: npm run build
40
-
41
- - name: Test
42
- if: steps.rpr.outputs.is_release_pr == 'false'
43
- run: npm test --if-present
@@ -1,158 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
-
8
- concurrency: ${{ github.workflow }}-${{ github.ref }}
9
-
10
- jobs:
11
- release:
12
- name: Publish Release
13
- runs-on: ubuntu-latest
14
- permissions:
15
- contents: write
16
- pull-requests: write
17
- # `--provenance` signs the tarball with a GitHub OIDC token, so the job must be able to
18
- # mint one. This does NOT switch the package to OIDC trusted publishing — authentication
19
- # is still the org npm token.
20
- id-token: write
21
- steps:
22
- # Author the changesets "Version Packages" PR as the org release App (not the default
23
- # GITHUB_TOKEN) so its CI runs without a manual "Approve and run" gate. Requires the org
24
- # App installed on this repo + org secrets RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY.
25
- - name: Generate GitHub App token
26
- id: app-token
27
- uses: actions/create-github-app-token@v2
28
- with:
29
- app-id: ${{ secrets.RELEASE_APP_ID }}
30
- private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
31
-
32
- - name: Checkout
33
- uses: actions/checkout@v4
34
- with:
35
- token: ${{ steps.app-token.outputs.token }}
36
-
37
- - name: Setup Node.js
38
- uses: actions/setup-node@v4
39
- with:
40
- node-version: 22.16.0
41
- registry-url: 'https://registry.npmjs.org'
42
-
43
- # `npm stage` landed in CLI 11.15.0; Node 22.16 ships npm 10.x, so the bundled one
44
- # cannot stage. Pinned rather than `@latest` so a future npm release cannot change how
45
- # releases behave without a commit here.
46
- - name: Use an npm that can stage
47
- run: npm i -g npm@^11.15.0
48
-
49
- - name: Install dependencies
50
- run: npm ci --legacy-peer-deps
51
-
52
- - name: Build
53
- run: npm run build
54
-
55
- # No `publish:` — changesets only opens/updates the version PR here. Publishing is the
56
- # step below, because `changeset publish` shells out to `npm publish`, which a
57
- # stage-only token cannot do (changesets/changesets#2025 is open for first-class
58
- # support). Splitting them keeps changesets doing what it is good at — versions and
59
- # changelogs — and leaves the registry call to us.
60
- - name: Create release Pull Request
61
- id: changesets
62
- uses: changesets/action@v1
63
- with:
64
- title: 'chore: release'
65
- commit: 'chore: release'
66
- env:
67
- GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
68
-
69
- # Stage the version for a human to approve.
70
- #
71
- # A staged version is uploaded but NOT public: a maintainer reviews it and approves with
72
- # 2FA, from the Staged Packages tab in the npm user menu or `npm stage approve`. That is
73
- # the point — every release keeps a proof-of-presence step even though CI is
74
- # unattended — and it is why the org token is scoped "read and write (stage only)".
75
- #
76
- # Idempotent: this workflow runs on every push to main, including the merge of the
77
- # version PR and any later commit, so it must do nothing when the version is already on
78
- # the registry or already sitting in the queue.
79
- - name: Stage the release for approval
80
- if: steps.changesets.outputs.hasChangesets != 'true'
81
- env:
82
- NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
83
- NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
84
- run: |
85
- set -euo pipefail
86
- name=$(node -p "require('./package.json').name")
87
- version=$(node -p "require('./package.json').version")
88
- private=$(node -p "require('./package.json').private === true")
89
- if [ "$private" = "true" ]; then
90
- echo "$name is private — nothing to publish."; exit 0
91
- fi
92
-
93
- if npm view "$name@$version" version >/dev/null 2>&1; then
94
- echo "$name@$version is already on the registry — nothing to do."
95
- exit 0
96
- fi
97
-
98
- if npm stage list "$name" 2>/dev/null | grep -q "$version"; then
99
- echo "$name@$version is already staged and waiting for approval."
100
- exit 0
101
- fi
102
-
103
- echo "Staging $name@$version…"
104
- # Capture the output: `npm stage publish` prints the stage id, and `npm stage
105
- # approve` takes ONLY that id — a package spec is rejected with "stage-id must be a
106
- # valid UUID". Without this the notice below hands out a command that does not work.
107
- out=$(npm stage publish --access public --provenance 2>&1 | tee /dev/stderr)
108
- uuid='[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
109
- # `|| true` because the script runs under `set -euo pipefail`: a grep that matches
110
- # nothing exits 1 and would fail the step even though the staging itself succeeded.
111
- stage_id=$(printf '%s' "$out" | grep -oE "$uuid" | tail -1 || true)
112
-
113
- {
114
- echo "### 📦 \`$name@$version\` is staged, not published"
115
- echo
116
- echo "It is uploaded and held for review. **Approve it to make it public:**"
117
- echo
118
- if [ -n "$stage_id" ]; then
119
- echo "- **CLI:** \`npm stage approve $stage_id\`"
120
- else
121
- echo "- **CLI:** \`npm stage list $name\` to find the id, then"
122
- echo " \`npm stage approve <stage-id>\`"
123
- fi
124
- echo "- **Web:** [npmjs.com](https://www.npmjs.com) → your avatar → **Staged"
125
- echo " Packages** → **Approve**. The button stays disabled until npm has"
126
- echo " finished scanning the tarball; the page refreshes every minute."
127
- echo
128
- echo "Both prompt for 2FA. Until then \`npm install $name\` still resolves"
129
- echo "\`$(npm view "$name" version 2>/dev/null || echo 'nothing')\`."
130
- } >> "$GITHUB_STEP_SUMMARY"
131
-
132
- if [ -n "$stage_id" ]; then
133
- echo "::notice title=Approval needed::$name@$version is staged. Run: npm stage approve $stage_id"
134
- else
135
- echo "::notice title=Approval needed::$name@$version is staged, but the stage id could not be read from the output. Run: npm stage list $name"
136
- fi
137
-
138
- # When changesets opened/updated the version PR, merge it so the release reaches the
139
- # staging queue without a manual click. App token → the merge's push re-triggers this
140
- # workflow, which then stages. DIRECT-merge (not `--auto`): GitHub
141
- # auto-merge ignores branch-protection review-bypass allowances; main is check-only so a
142
- # direct merge lands once the required check is green — retry until then.
143
- - name: Merge the version PR to stage the release
144
- if: steps.changesets.outputs.hasChangesets == 'true'
145
- env:
146
- GH_TOKEN: ${{ steps.app-token.outputs.token }}
147
- run: |
148
- pr=$(gh pr list --base main --head changeset-release/main --state open --json number --jq '.[0].number // empty')
149
- if [ -z "$pr" ]; then echo "No open version PR (nothing to merge)."; exit 0; fi
150
- for i in $(seq 1 40); do
151
- if gh pr merge "$pr" --merge 2>/tmp/merge_err; then
152
- echo "Merged version PR #$pr on attempt $i."
153
- exit 0
154
- fi
155
- echo "Attempt $i/40: version PR #$pr not mergeable yet ($(tr -d '\n' < /tmp/merge_err)); waiting for the required check…"
156
- sleep 15
157
- done
158
- echo "::warning::Version PR #$pr not merged after retries — merge it manually."
package/CHANGELOG.md DELETED
@@ -1,30 +0,0 @@
1
- # @\_linked/ui
2
-
3
- ## 2.0.0
4
-
5
- ### Major Changes
6
-
7
- - [#7](https://github.com/linked-cm/ui/pull/7) [`be0c4ef`](https://github.com/linked-cm/ui/commit/be0c4ef095d30cd1388517f7d13383eb26b4926c) Thanks [@flyon](https://github.com/flyon)! - Rename from `@_linked/ui` to `@_linked/shape-ui`.
8
-
9
- `ui` said only "not backend", which is the least informative name available for the layer
10
- most in need of one that states its contract. Every component here takes a shape and a
11
- property shape and renders a control for it, working on any shape rather than a known one.
12
- `shape-ui` says that.
13
-
14
- Renaming now because it is nearly free: the package has three consumers. It becomes
15
- expensive as soon as it has more, and this layer should have more.
16
-
17
- Also removes nine compiled CommonJS files that had been committed into `src/` alongside their
18
- `.tsx` sources, and a dead `ShapeTable.tsx.old`.
19
-
20
- ## 1.0.3
21
-
22
- ### Patch Changes
23
-
24
- - [#3](https://github.com/linked-cm/ui/pull/3) [`24d8c4b`](https://github.com/linked-cm/ui/commit/24d8c4bdd0e97d01dacdda8dcc94b17cfad84ca8) Thanks [@flyon](https://github.com/flyon)! - loadData: ESM-only JSON import — drop the dead CJS branch, add the `{ with: { type: 'json' } }` import attribute.
25
-
26
- ## 1.0.2
27
-
28
- ### Patch Changes
29
-
30
- - [`56ee32e`](https://github.com/linked-cm/ui/commit/56ee32ef1e709b8b6841870c53a73e285c68b6c7) - Initial release under the new publishing setup.
@@ -1,20 +0,0 @@
1
- import React from 'react';
2
- import { Shape } from '@_linked/core/shapes/Shape';
3
- import { PropertyShape } from '@_linked/core/shapes/SHACL';
4
- export interface AvatarEditorProps {
5
- of?: Shape;
6
- property?: PropertyShape;
7
- /**
8
- * Converts the value returned by the shape, to the input string for the avatar
9
- * @param shapePropertyValue
10
- * @returns
11
- */
12
- preConvertValueFn?: (shapePropertyValue: any) => string;
13
- /**
14
- * Converts the value of the avatar to a value that can be provided to the set method of the shape
15
- * @param inputValue
16
- * @returns
17
- */
18
- postConvertValueFn?: (inputValue: string) => any;
19
- }
20
- export declare const AvatarEditor: ({ of, property, preConvertValueFn, postConvertValueFn, ...restProps }: AvatarEditorProps) => React.JSX.Element;
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
- import { Root, Image } from '@_linked/primitives/components/Avatar';
3
- export const AvatarEditor = ({ of, property, preConvertValueFn, postConvertValueFn, ...restProps }) => {
4
- return (React.createElement(Root, null,
5
- React.createElement(Image, null)));
6
- };
7
- //# sourceMappingURL=AvatarEditor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AvatarEditor.js","sourceRoot":"","sources":["../../../src/components/AvatarEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,uCAAuC,CAAC;AAoBpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAC3B,EAAE,EACF,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,GAAG,SAAS,EACM,EAAE,EAAE;IACtB,OAAO,CACL,oBAAC,IAAI;QACH,oBAAC,KAAK,OAAG,CACJ,CACR,CAAC;AACJ,CAAC,CAAC"}
@@ -1,21 +0,0 @@
1
- import React from 'react';
2
- import { Shape } from '@_linked/core/shapes/Shape';
3
- import { PropertyShape } from '@_linked/core/shapes/SHACL';
4
- export interface CheckboxEditorProps {
5
- onCheckedChange?: (e: any) => void;
6
- of?: Shape;
7
- property?: PropertyShape;
8
- /**
9
- * Converts the value returned by the shape, to the input string for the checkbox
10
- * @param shapePropertyValue
11
- * @returns
12
- */
13
- preConvertValueFn?: (shapePropertyValue: any) => string;
14
- /**
15
- * Converts the value of the checkbox to a value that can be provided to the set method of the shape
16
- * @param inputValue
17
- * @returns
18
- */
19
- postConvertValueFn?: (inputValue: string) => any;
20
- }
21
- export declare const CheckboxEditor: ({ onCheckedChange, of, property, preConvertValueFn, postConvertValueFn, ...restProps }: CheckboxEditorProps) => React.JSX.Element;
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
- import { Checkbox } from '@_linked/primitives/components/Checkbox';
3
- import style from './CheckboxEditor.module.css';
4
- export const CheckboxEditor = ({ onCheckedChange, of, property, preConvertValueFn, postConvertValueFn, ...restProps }) => {
5
- const currentValues = of[property.label];
6
- const onChangeHandler = (item, checked) => {
7
- if (!of)
8
- return;
9
- if (property.in) {
10
- if (checked) {
11
- currentValues.add(item);
12
- }
13
- else {
14
- currentValues.delete(item);
15
- }
16
- }
17
- else {
18
- of[property.label] = checked;
19
- }
20
- if (onCheckedChange) {
21
- onCheckedChange(currentValues);
22
- }
23
- };
24
- return (React.createElement(React.Fragment, null, property.in ? (property.in.map((item, i) => {
25
- return (React.createElement("label", { className: style.Root },
26
- React.createElement(Checkbox, { value: of[property.label], onCheckedChange: onChangeHandler.bind(this, item), defaultChecked: currentValues.has(item), ...restProps }),
27
- "TODO: ",
28
- item.toString()));
29
- })) : (React.createElement("div", null, "checkbox usage unimplemented")
30
- // <Checkbox
31
- // value={of[property.label]}
32
- // onCheckedChange={onChangeHandler}
33
- // defaultChecked={
34
- // of
35
- // ? preConvertValueFn
36
- // ? preConvertValueFn(of[property.label])
37
- // : of[property.label]
38
- // : ''
39
- // }
40
- // {...restProps}
41
- // />
42
- )));
43
- };
44
- //# sourceMappingURL=CheckboxEditor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CheckboxEditor.js","sourceRoot":"","sources":["../../../src/components/CheckboxEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,yCAAyC,CAAC;AAEnE,OAAO,KAAK,MAAM,6BAA6B,CAAC;AAqBhD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,eAAe,EACf,EAAE,EACF,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,GAAG,SAAS,EACQ,EAAE,EAAE;IACxB,MAAM,aAAa,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEzC,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;QACxC,IAAI,CAAC,EAAE;YAAE,OAAO;QAEhB,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,IAAI,OAAO,EAAE,CAAC;gBACZ,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;QAC/B,CAAC;QAED,IAAI,eAAe,EAAE,CAAC;YACpB,eAAe,CAAC,aAAa,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,0CACG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CACb,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QAC1B,OAAO,CACL,+BAAO,SAAS,EAAE,KAAK,CAAC,IAAI;YAC1B,oBAAC,QAAQ,IACP,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EACzB,eAAe,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EACjD,cAAc,EAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KACnC,SAAS,GACb;;YACK,IAAI,CAAC,QAAQ,EAAE,CAChB,CACT,CAAC;IACJ,CAAC,CAAC,CACH,CAAC,CAAC,CAAC,CACF,gEAAuC;IACvC,YAAY;IACZ,+BAA+B;IAC/B,sCAAsC;IACtC,qBAAqB;IACrB,SAAS;IACT,4BAA4B;IAC5B,kDAAkD;IAClD,+BAA+B;IAC/B,aAAa;IACb,MAAM;IACN,mBAAmB;IACnB,KAAK;KACN,CACA,CACJ,CAAC;AACJ,CAAC,CAAC"}
@@ -1,20 +0,0 @@
1
- import React from 'react';
2
- import { Shape } from '@_linked/core/shapes/Shape';
3
- export interface DateEditorProps {
4
- onChange?: (e: any) => void;
5
- of?: Shape;
6
- property?: string;
7
- /**
8
- * Converts the value returned by the shape, to the date string for the date field
9
- * @param shapePropertyValue
10
- * @returns
11
- */
12
- preConvertValueFn?: (shapePropertyValue: any) => string;
13
- /**
14
- * Converts the value of the date field to a value that can be provided to the set method of the shape
15
- * @param inputValue
16
- * @returns
17
- */
18
- postConvertValueFn?: (inputValue: string) => any;
19
- }
20
- export declare const DateEditor: ({ onChange, of, property, preConvertValueFn, postConvertValueFn, ...restProps }: DateEditorProps) => React.JSX.Element;
@@ -1,22 +0,0 @@
1
- import React from 'react';
2
- export const DateEditor = ({ onChange, of, property, preConvertValueFn, postConvertValueFn, ...restProps }) => {
3
- const onChangeHandler = (e) => {
4
- if (onChange) {
5
- onChange(e);
6
- }
7
- };
8
- return (React.createElement(React.Fragment, null)
9
- // <Input
10
- // onChange={onChangeHandler}
11
- // defaultValue={
12
- // of
13
- // ? preConvertValueFn
14
- // ? preConvertValueFn(of[property])
15
- // : of[property]
16
- // : ''
17
- // }
18
- // {...restProps}
19
- // />
20
- );
21
- };
22
- //# sourceMappingURL=DateEditor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DateEditor.js","sourceRoot":"","sources":["../../../src/components/DateEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAqBxC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EACzB,QAAQ,EACR,EAAE,EACF,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAElB,GAAG,SAAS,EACI,EAAE,EAAE;IACpB,MAAM,eAAe,GAAG,CAAC,CAAC,EAAE,EAAE;QAC5B,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,yCAAK;IACL,SAAS;IACT,+BAA+B;IAC/B,mBAAmB;IACnB,SAAS;IACT,4BAA4B;IAC5B,4CAA4C;IAC5C,yBAAyB;IACzB,aAAa;IACb,MAAM;IACN,mBAAmB;IACnB,KAAK;KACN,CAAC;AACJ,CAAC,CAAC"}
@@ -1,23 +0,0 @@
1
- import React from 'react';
2
- import { Shape } from '@_linked/core/shapes/Shape';
3
- import { PropertyShape } from '@_linked/core/shapes/SHACL';
4
- export interface RadioButtonProps {
5
- onValueChange?: (newValue: any) => void;
6
- of?: Shape;
7
- property?: PropertyShape;
8
- children?: React.ReactNode;
9
- className?: string;
10
- /**
11
- * Converts the value returned by the shape, to the radio input string for the radio input
12
- * @param shapePropertyValue
13
- * @returns
14
- */
15
- preConvertValueFn?: (shapePropertyValue: any) => string;
16
- /**
17
- * Converts the value of the radio input to a value that can be provided to the set method of the shape
18
- * @param radioInputValue
19
- * @returns
20
- */
21
- postConvertValueFn?: (radioInputValue: string) => any;
22
- }
23
- export declare const RadioButtonEditor: React.FC<RadioButtonProps>;
@@ -1,41 +0,0 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { RadioGroup } from '@_linked/primitives/components/RadioGroup';
3
- import styles from './RadioButtonEditor.module.css';
4
- import { cl } from '@_linked/react/utils/ClassNames';
5
- export const RadioButtonEditor = ({ onValueChange, of, property, preConvertValueFn, postConvertValueFn, children, className, ...props }) => {
6
- const [labelHasLongString, setLabelHasLongString] = useState(false);
7
- const onChangeHandler = (newValue) => {
8
- if (!of)
9
- return;
10
- of[property.label] = newValue;
11
- if (onValueChange) {
12
- onValueChange(of[property.label]);
13
- // onValueChange(
14
- // options.map((option) => {
15
- // console.log(option, 'option');
16
- // of[property.label] = newValue;
17
- // }),
18
- // );
19
- }
20
- };
21
- useEffect(() => {
22
- //check if radio button label has a long string
23
- if (property.in) {
24
- const containsLongString = property.in.some((list) => {
25
- throw Error('TODO: property.in for RadioButtonEditor');
26
- // const wordCount = list.value.split(' ').length;
27
- // return wordCount > 5;
28
- });
29
- setLabelHasLongString(containsLongString);
30
- }
31
- }, [property.in]);
32
- // function cl(radioButtonField: any, arg1: any): string {
33
- // throw new Error('Function not implemented.');
34
- // }
35
- console.log('of.label', of[property.label]);
36
- return (React.createElement(React.Fragment, null,
37
- React.createElement(RadioGroup.Root, { defaultValue: "default", "aria-label": "View density", className: cl(styles.Root, labelHasLongString && styles.columnBased) }, property.in ? (property.in.map((item) => (React.createElement("label", { className: styles.radioButtonItem },
38
- React.createElement(RadioGroup.Item, { value: item.toString(), className: styles.radioButton }),
39
- "TODO: item from property.in")))) : (React.createElement("p", null, "unimplemented single radio button")))));
40
- };
41
- //# sourceMappingURL=RadioButtonEditor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RadioButtonEditor.js","sourceRoot":"","sources":["../../../src/components/RadioButtonEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEnD,OAAO,EAAc,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAEnF,OAAO,MAAM,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AAsBrD,MAAM,CAAC,MAAM,iBAAiB,GAA+B,CAAC,EAC5D,aAAa,EACb,EAAE,EACF,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,EAAE;IACH,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpE,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,EAAE;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;QAE9B,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YAClC,iBAAiB;YACjB,8BAA8B;YAC9B,qCAAqC;YACrC,qCAAqC;YACrC,QAAQ;YACR,KAAK;QACP,CAAC;IACH,CAAC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,+CAA+C;QAC/C,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,MAAM,kBAAkB,GAAG,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;gBACnD,MAAM,KAAK,CAAC,yCAAyC,CAAC,CAAC;gBACvD,kDAAkD;gBAClD,wBAAwB;YAC1B,CAAC,CAAC,CAAC;YACH,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAElB,0DAA0D;IAC1D,kDAAkD;IAClD,IAAI;IAEJ,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAE5C,OAAO,CACL;QACE,oBAAC,UAAU,CAAC,IAAI,IACd,YAAY,EAAC,SAAS,gBACX,cAAc,EACzB,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,IAAI,MAAM,CAAC,WAAW,CAAC,IAEnE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CACb,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACxB,+BAAO,SAAS,EAAE,MAAM,CAAC,eAAe;YACtC,oBAAC,UAAU,CAAC,IAAI,IACd,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,EACtB,SAAS,EAAE,MAAM,CAAC,WAAW,GAC7B;0CAGI,CACT,CAAC,CACH,CAAC,CAAC,CAAC,CACF,mEAAwC,CACzC,CACe,CACjB,CACJ,CAAC;AACJ,CAAC,CAAC"}