@shwfed/config 2.12.1 → 2.12.3

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 (142) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +10 -0
  3. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +68 -68
  4. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +68 -68
  5. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +68 -68
  6. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +68 -68
  7. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +34 -34
  8. package/dist/runtime/components/form/config.vue +17 -6
  9. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.d.vue.ts +2 -2
  10. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.vue.d.ts +2 -2
  11. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +6 -6
  12. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +6 -6
  13. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +8 -8
  14. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +8 -8
  15. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
  16. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
  17. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.d.vue.ts +2 -2
  18. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.vue.d.ts +2 -2
  19. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +8 -1
  20. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +24 -24
  21. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +24 -24
  22. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +6 -6
  23. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +6 -6
  24. package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.d.vue.ts +2 -2
  25. package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.vue.d.ts +2 -2
  26. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +24 -24
  27. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +24 -24
  28. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +22 -22
  29. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +22 -22
  30. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/config.d.vue.ts +32 -32
  31. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/config.vue.d.ts +32 -32
  32. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/runtime.vue +10 -2
  33. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/config.d.vue.ts +32 -32
  34. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/config.vue.d.ts +32 -32
  35. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/runtime.vue +10 -2
  36. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +24 -24
  37. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +24 -24
  38. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/runtime.vue +19 -1
  39. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +22 -22
  40. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +22 -22
  41. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/runtime.vue +19 -1
  42. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +24 -24
  43. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +24 -24
  44. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/runtime.vue +20 -1
  45. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +22 -22
  46. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +22 -22
  47. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/runtime.vue +20 -1
  48. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/config.d.vue.ts +32 -32
  49. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/config.vue.d.ts +32 -32
  50. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/runtime.vue +10 -2
  51. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/config.d.vue.ts +32 -32
  52. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/config.vue.d.ts +32 -32
  53. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/runtime.vue +10 -2
  54. package/dist/runtime/components/form/index.vue +6 -3
  55. package/dist/runtime/components/form/utils/form-vars.js +2 -1
  56. package/dist/runtime/components/head.d.vue.ts +13 -0
  57. package/dist/runtime/components/head.vue +69 -0
  58. package/dist/runtime/components/head.vue.d.ts +13 -0
  59. package/dist/runtime/components/sidebar/config.d.vue.ts +7 -0
  60. package/dist/runtime/components/sidebar/config.vue +302 -0
  61. package/dist/runtime/components/sidebar/config.vue.d.ts +7 -0
  62. package/dist/runtime/components/sidebar/favorites.d.ts +18 -0
  63. package/dist/runtime/components/sidebar/favorites.js +20 -0
  64. package/dist/runtime/components/sidebar/footer.d.vue.ts +11 -0
  65. package/dist/runtime/components/sidebar/footer.vue +49 -0
  66. package/dist/runtime/components/sidebar/footer.vue.d.ts +11 -0
  67. package/dist/runtime/components/sidebar/index.d.vue.ts +50 -0
  68. package/dist/runtime/components/sidebar/index.vue +100 -0
  69. package/dist/runtime/components/sidebar/index.vue.d.ts +50 -0
  70. package/dist/runtime/components/sidebar/node.d.vue.ts +9 -0
  71. package/dist/runtime/components/sidebar/node.vue +102 -0
  72. package/dist/runtime/components/sidebar/node.vue.d.ts +9 -0
  73. package/dist/runtime/components/sidebar/schema.d.ts +72 -0
  74. package/dist/runtime/components/sidebar/schema.js +84 -0
  75. package/dist/runtime/components/sidebar/tree.d.vue.ts +10 -0
  76. package/dist/runtime/components/sidebar/tree.vue +260 -0
  77. package/dist/runtime/components/sidebar/tree.vue.d.ts +10 -0
  78. package/dist/runtime/components/sidebar/types.d.ts +19 -0
  79. package/dist/runtime/components/sidebar/types.js +0 -0
  80. package/dist/runtime/components/sidebar/use-editor.d.ts +18 -0
  81. package/dist/runtime/components/sidebar/use-editor.js +43 -0
  82. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +2 -2
  83. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +2 -2
  84. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +2 -2
  85. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +2 -2
  86. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.d.vue.ts +36 -36
  87. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.vue.d.ts +36 -36
  88. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +40 -40
  89. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +40 -40
  90. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.d.vue.ts +36 -36
  91. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.vue.d.ts +36 -36
  92. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +40 -40
  93. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +40 -40
  94. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
  95. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
  96. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  97. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  98. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +26 -26
  99. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +26 -26
  100. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +26 -26
  101. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +26 -26
  102. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
  103. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
  104. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  105. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  106. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +26 -26
  107. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +26 -26
  108. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +26 -26
  109. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +26 -26
  110. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
  111. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
  112. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  113. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  114. package/dist/runtime/components/table/schema.d.ts +68 -68
  115. package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
  116. package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
  117. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
  118. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
  119. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
  120. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
  121. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
  122. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
  123. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.d.vue.ts +1 -0
  124. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue +8 -0
  125. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue.d.ts +1 -0
  126. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +3 -2
  127. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +51 -9
  128. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +3 -2
  129. package/dist/runtime/components/ui/expression-editor/option-chip-extension.d.ts +4 -0
  130. package/dist/runtime/components/ui/expression-editor/option-chip-extension.js +119 -0
  131. package/dist/runtime/components/ui/expression-editor/option-refs.d.ts +6 -0
  132. package/dist/runtime/components/ui/expression-editor/option-refs.js +24 -0
  133. package/dist/runtime/components/ui/expression-editor/picker-entries.d.ts +2 -1
  134. package/dist/runtime/components/ui/expression-editor/picker-entries.js +12 -2
  135. package/dist/runtime/utils/cel-context.d.ts +1 -0
  136. package/dist/runtime/utils/cel-context.js +1 -0
  137. package/dist/runtime/utils/selections-registry.d.ts +40 -6
  138. package/dist/runtime/utils/selections-registry.js +19 -4
  139. package/dist/runtime/vendor/cel-js/CLAUDE.md +2 -0
  140. package/dist/runtime/vendor/cel-js/PROMPT.md +2 -0
  141. package/dist/runtime/vendor/cel-js/lib/functions.js +33 -0
  142. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ <script setup>
2
+ import { Icon } from "@iconify/vue";
3
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible";
4
+ import {
5
+ SidebarMenuAction,
6
+ SidebarMenuButton,
7
+ SidebarMenuItem,
8
+ SidebarMenuSub
9
+ } from "../ui/sidebar";
10
+ defineOptions({ name: "ShwfedSidebarNode" });
11
+ const props = defineProps({
12
+ node: { type: Object, required: true },
13
+ favorites: { type: null, required: true }
14
+ });
15
+ function onOpenUpdate(open) {
16
+ props.favorites.setOpen(props.node.key, open);
17
+ }
18
+ </script>
19
+
20
+ <template>
21
+ <SidebarMenuItem>
22
+ <!-- Group: collapsible, recurses into a sub-menu -->
23
+ <Collapsible
24
+ v-if="node.children.length"
25
+ class="group/collapsible"
26
+ :open="favorites.isOpen(node.key, true)"
27
+ @update:open="onOpenUpdate"
28
+ >
29
+ <CollapsibleTrigger as-child>
30
+ <SidebarMenuButton>
31
+ <Icon
32
+ v-if="node.icon"
33
+ :icon="node.icon"
34
+ />
35
+ <span>{{ node.label }}</span>
36
+ <Icon
37
+ icon="fluent:chevron-right-20-regular"
38
+ class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
39
+ />
40
+ </SidebarMenuButton>
41
+ </CollapsibleTrigger>
42
+ <CollapsibleContent>
43
+ <SidebarMenuSub>
44
+ <ShwfedSidebarNode
45
+ v-for="child in node.children"
46
+ :key="child.key"
47
+ :node="child"
48
+ :favorites="favorites"
49
+ />
50
+ </SidebarMenuSub>
51
+ </CollapsibleContent>
52
+ </Collapsible>
53
+
54
+ <!-- Link: navigable leaf with a route, star-favoritable -->
55
+ <template v-else-if="node.route">
56
+ <NuxtLink
57
+ :to="node.route"
58
+ custom
59
+ >
60
+ <template #default="{ navigate, isExactActive }">
61
+ <SidebarMenuButton
62
+ as-child
63
+ :is-active="isExactActive"
64
+ >
65
+ <button
66
+ type="button"
67
+ @click="navigate"
68
+ >
69
+ <Icon
70
+ v-if="node.icon"
71
+ :icon="node.icon"
72
+ />
73
+ <span>{{ node.label }}</span>
74
+ </button>
75
+ </SidebarMenuButton>
76
+ </template>
77
+ </NuxtLink>
78
+ <SidebarMenuAction
79
+ v-if="node.favoritable"
80
+ :show-on-hover="!favorites.isFavorite(node.key)"
81
+ class="text-yellow-500!"
82
+ @click.stop="favorites.toggleFavorite(node.key)"
83
+ >
84
+ <Icon :icon="favorites.isFavorite(node.key) ? 'fluent:star-20-filled' : 'fluent:star-20-regular'" />
85
+ </SidebarMenuAction>
86
+ </template>
87
+
88
+ <!-- Plain label: childless, no route -->
89
+ <SidebarMenuButton
90
+ v-else
91
+ as-child
92
+ >
93
+ <span>
94
+ <Icon
95
+ v-if="node.icon"
96
+ :icon="node.icon"
97
+ />
98
+ <span>{{ node.label }}</span>
99
+ </span>
100
+ </SidebarMenuButton>
101
+ </SidebarMenuItem>
102
+ </template>
@@ -0,0 +1,9 @@
1
+ import type { NavNode } from './types.js';
2
+ import type { SidebarFavorites } from './favorites.js';
3
+ type __VLS_Props = {
4
+ node: NavNode;
5
+ favorites: SidebarFavorites;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,72 @@
1
+ import { Schema } from 'effect';
2
+ import type { Environment } from '../../vendor/cel-js/lib/index.js';
3
+ import type { LocaleValue } from '../../share/locale.js';
4
+ export { getStructFieldDescription, getStructFieldTitle } from '../table/utils/schema-meta.js';
5
+ declare const KIND = "shwfed.component.sidebar";
6
+ /**
7
+ * Standalone, data-driven navigation sidebar. A PEER of the page config
8
+ * (`ShwfedConfig`), NOT a block inside it: its own serializable `kind`, its own
9
+ * editor modal, its own CEL context. It never touches `blockRef` / `PageConfig`
10
+ * / the block registry.
11
+ *
12
+ * The navigation TREE is data-driven, modeled on the `tree.single` block: one
13
+ * `DataSource` returns the whole node payload, and per-node CEL expressions
14
+ * (sharing a `node` var) walk and render it. Arbitrary-depth nesting and
15
+ * CEL-computed link targets fall out of `nodeChildren` / `nodeRoute` — there is
16
+ * no hand-authored static tree. A node with children renders collapsible; a
17
+ * node whose `nodeRoute` resolves non-empty renders as a navigable link (and is
18
+ * favoritable — the star is a runtime affordance, not a schema field).
19
+ */
20
+ export declare function SidebarConfig(configure: (env: Environment) => void): Schema.Struct<{
21
+ kind: Schema.tag<"shwfed.component.sidebar">;
22
+ dataSource: Schema.Struct<{
23
+ request: Schema.optional<Schema.Schema<string, string, never>>;
24
+ data: Schema.Schema<string, string, never>;
25
+ }>;
26
+ nodeKey: Schema.Schema<string, string, never>;
27
+ nodeChildren: Schema.Schema<string, string, never>;
28
+ nodeLabel: Schema.refine<readonly [{
29
+ readonly locale: "zh";
30
+ readonly message: string;
31
+ }, ...{
32
+ readonly locale: "en" | "ja" | "ko";
33
+ readonly message: string;
34
+ }[]], Schema.suspend<readonly [{
35
+ readonly locale: "zh";
36
+ readonly message: string;
37
+ }, ...{
38
+ readonly locale: "en" | "ja" | "ko";
39
+ readonly message: string;
40
+ }[]], string | readonly [{
41
+ readonly locale: "zh";
42
+ readonly message: string;
43
+ }, ...{
44
+ readonly locale: "en" | "ja" | "ko";
45
+ readonly message: string;
46
+ }[]], never>>;
47
+ nodeIcon: Schema.optional<Schema.Schema<string, string, never>>;
48
+ nodeRoute: Schema.optional<Schema.Schema<string, string, never>>;
49
+ nodeVisible: Schema.optional<Schema.Schema<string, string, never>>;
50
+ style: Schema.optional<Schema.Schema<string, string, never>>;
51
+ }>;
52
+ /**
53
+ * Creation-time defaults for a fresh sidebar. Seeds working expressions (like
54
+ * the tree block's `defaultBody`) so a freshly-added sidebar renders a sane
55
+ * skeleton the designer can immediately edit against real data shapes.
56
+ */
57
+ export declare function defaults(): Omit<SidebarConfigValue, 'kind'>;
58
+ export declare function createSidebarConfig(body?: Partial<Omit<SidebarConfigValue, 'kind'>>): SidebarConfigValue;
59
+ export type SidebarConfigValue = Readonly<{
60
+ kind: typeof KIND;
61
+ dataSource: Readonly<{
62
+ request?: string;
63
+ data: string;
64
+ }>;
65
+ nodeKey: string;
66
+ nodeChildren: string;
67
+ nodeLabel: LocaleValue;
68
+ nodeIcon?: string;
69
+ nodeRoute?: string;
70
+ nodeVisible?: string;
71
+ style?: string;
72
+ }>;
@@ -0,0 +1,84 @@
1
+ import { Schema } from "effect";
2
+ import { Expression, LocaleMarkdown } from "../../share/expression.js";
3
+ import { registerNodeVariableIfAbsent } from "../../share/tree-node-var.js";
4
+ import { DataSource } from "../table/schema.js";
5
+ export { getStructFieldDescription, getStructFieldTitle } from "../table/utils/schema-meta.js";
6
+ const KIND = "shwfed.component.sidebar";
7
+ export function SidebarConfig(configure) {
8
+ const nodeConfigure = (env) => {
9
+ configure(env);
10
+ registerNodeVariableIfAbsent(env);
11
+ };
12
+ const CelNodeKey = Expression({
13
+ configure: nodeConfigure,
14
+ resultType: (t) => t === "string" || t === "number" || t === "dyn"
15
+ });
16
+ const CelNodeChildren = Expression({
17
+ configure: nodeConfigure,
18
+ // Accept lists, optional lists, and `dyn` — CEL infers an empty literal
19
+ // such as `[]` as `list<dyn>`; a configured expression like
20
+ // `has(node.kids) ? node.kids : null` lands on `dyn`. Mirrors the table's
21
+ // `data` expression.
22
+ resultType: (t) => t === "dyn" || t.startsWith("list") || t.startsWith("optional")
23
+ });
24
+ const NodeLocaleMd = LocaleMarkdown({ configure: nodeConfigure });
25
+ const CelNodeString = Expression({
26
+ configure: nodeConfigure,
27
+ resultType: (t) => t === "string" || t === "dyn" || t.startsWith("optional")
28
+ });
29
+ const CelNodeBool = Expression({ configure: nodeConfigure, resultType: "bool" });
30
+ const CelStyle = Expression({
31
+ configure,
32
+ resultType: (t) => t === "string" || t === "dyn" || t.startsWith("map") || t.startsWith("optional")
33
+ });
34
+ return Schema.Struct({
35
+ kind: Schema.tag(KIND),
36
+ dataSource: DataSource(configure).annotations({
37
+ title: "\u6570\u636E\u6E90",
38
+ description: "\u8FD4\u56DE\u5B8C\u6574\u5BFC\u822A\u6811\uFF08\u6839\u8282\u70B9\u5217\u8868\uFF09\u7684\u6570\u636E\u6E90\uFF1B\u4FA7\u680F\u901A\u8FC7 `\u8282\u70B9\u5B50\u7EA7` \u8868\u8FBE\u5F0F\u9010\u5C42\u5411\u4E0B\u8BFB\u53D6\u5B50\u7EA7"
39
+ }),
40
+ nodeKey: CelNodeKey.annotations({
41
+ title: "\u8282\u70B9 ID",
42
+ description: "\u4E3A\u6BCF\u4E2A\u8282\u70B9\u8BA1\u7B97\u7A33\u5B9A\u552F\u4E00 ID \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u8FD0\u884C\u65F6\u4EE5\u5B57\u7B26\u4E32\u5F62\u5F0F\u5B58\u50A8\uFF0C\u4E5F\u7528\u4F5C\u6536\u85CF\u6807\u8BC6"
43
+ }),
44
+ nodeChildren: CelNodeChildren.annotations({
45
+ title: "\u8282\u70B9\u5B50\u7EA7",
46
+ description: "\u8FD4\u56DE\u5F53\u524D\u8282\u70B9\u7684\u5B50\u7EA7\u5217\u8868\u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u8FD4\u56DE `none` / `null` \u89C6\u4E3A\u53F6\u5B50\u8282\u70B9"
47
+ }),
48
+ nodeLabel: NodeLocaleMd.annotations({
49
+ title: "\u8282\u70B9\u6807\u7B7E",
50
+ description: "\u8282\u70B9\u5C55\u793A\u7684\u672C\u5730\u5316\u6587\u672C\uFF0C\u652F\u6301 Markdown \u4E0E `{{ node.foo }}` \u63D2\u503C"
51
+ }),
52
+ nodeIcon: Schema.optional(CelNodeString.annotations({
53
+ title: "\u8282\u70B9\u56FE\u6807",
54
+ description: "\u8FD4\u56DE Iconify \u56FE\u6807 ID\uFF08\u5982 `'fluent:home-20-regular'`\uFF09\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u7559\u7A7A\u5219\u4E0D\u663E\u793A\u56FE\u6807"
55
+ })),
56
+ nodeRoute: Schema.optional(CelNodeString.annotations({
57
+ title: "\u8282\u70B9\u94FE\u63A5",
58
+ description: "\u8FD4\u56DE\u76EE\u6807\u8DEF\u7531\uFF08`string`\uFF09\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u89E3\u6790\u4E3A\u975E\u7A7A\u65F6\u8BE5\u8282\u70B9\u6210\u4E3A\u53EF\u70B9\u51FB\u7684\u5BFC\u822A\u94FE\u63A5\uFF08\u5E76\u53EF\u88AB\u6536\u85CF\uFF09\uFF0C\u5426\u5219\u4EC5\u4F5C\u4E3A\u5206\u7EC4\u6807\u9898"
59
+ })),
60
+ nodeVisible: Schema.optional(CelNodeBool.annotations({
61
+ title: "\u8282\u70B9\u53EF\u89C1\u6761\u4EF6",
62
+ description: "\u8FD4\u56DE `true` \u8868\u793A\u8BE5\u8282\u70B9\uFF08\u53CA\u5176\u5B50\u6811\uFF09\u53EF\u89C1\uFF1B\u7559\u7A7A\u5219\u59CB\u7EC8\u53EF\u89C1\u3002\u5E38\u7528\u4E8E\u6309\u6743\u9650\u63A7\u5236\u5BFC\u822A\u9879"
63
+ })),
64
+ style: Schema.optional(CelStyle).annotations({
65
+ title: "\u6837\u5F0F",
66
+ description: "\u8FD4\u56DE\u5E94\u7528\u5728\u4FA7\u680F\u6839\u5143\u7D20\u4E0A\u7684\u6837\u5F0F\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8FD4\u56DE CSS \u5B57\u7B26\u4E32\u6216\u6837\u5F0F\u5BF9\u8C61\uFF1B\u7559\u7A7A\u5219\u4E0D\u8BBE\u989D\u5916\u6837\u5F0F"
67
+ })
68
+ }).annotations({
69
+ title: "SidebarConfig",
70
+ description: "\u6570\u636E\u9A71\u52A8\u7684\u5BFC\u822A\u4FA7\u680F\u5B8C\u6574\u914D\u7F6E"
71
+ });
72
+ }
73
+ export function defaults() {
74
+ return {
75
+ dataSource: { data: "[]" },
76
+ nodeKey: "string(node.id)",
77
+ nodeChildren: "has(node.children) ? node.children : null",
78
+ nodeLabel: [{ locale: "zh", message: "{{ node.label }}" }],
79
+ nodeRoute: "node.route"
80
+ };
81
+ }
82
+ export function createSidebarConfig(body) {
83
+ return { kind: KIND, ...defaults(), ...body };
84
+ }
@@ -0,0 +1,10 @@
1
+ import type { Environment } from '../../vendor/cel-js/lib/index.js';
2
+ import type { SidebarConfigValue } from './schema.js';
3
+ type __VLS_Props = {
4
+ config: SidebarConfigValue;
5
+ configure?: (env: Environment) => void;
6
+ storageKey: string;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,260 @@
1
+ <script setup>
2
+ import { Icon } from "@iconify/vue";
3
+ import { Effect, Fiber, Option } from "effect";
4
+ import { Fetch } from "fx-fetch";
5
+ import { computed, ref, watch } from "vue";
6
+ import { useI18n } from "vue-i18n";
7
+ import { fetchJsonOption } from "../../share/request";
8
+ import { cel as _rawCel } from "../../utils/cel";
9
+ import { celBindings, injectCELContext } from "../../utils/cel-context";
10
+ import { getLocalizedText } from "../../share/locale";
11
+ import { interpolateMarkdown } from "../table/utils/runtime";
12
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible";
13
+ import { SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu } from "../ui/sidebar";
14
+ import { Skeleton } from "../ui/skeleton";
15
+ import SidebarNode from "./node.vue";
16
+ import { useSidebarFavorites } from "./favorites";
17
+ defineOptions({ name: "ShwfedSidebarTree" });
18
+ const props = defineProps({
19
+ config: { type: Object, required: true },
20
+ configure: { type: Function, required: false },
21
+ storageKey: { type: String, required: true }
22
+ });
23
+ const { locale, t } = useI18n({
24
+ inheritLocale: true,
25
+ useScope: "global",
26
+ messages: {
27
+ zh: { "sidebar-favorites": "\u6536\u85CF" },
28
+ en: { "sidebar-favorites": "Favorites" },
29
+ ja: { "sidebar-favorites": "\u304A\u6C17\u306B\u5165\u308A" },
30
+ ko: { "sidebar-favorites": "\uC990\uACA8\uCC3E\uAE30" }
31
+ }
32
+ });
33
+ const favorites = useSidebarFavorites(props.storageKey);
34
+ const inherited = injectCELContext();
35
+ const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
36
+ const styleValue = computed(() => {
37
+ const expr = props.config.style;
38
+ if (!expr) return void 0;
39
+ try {
40
+ const r = Effect.runSync($cel(expr, {}));
41
+ if (r == null) return void 0;
42
+ if (typeof r === "string") return r;
43
+ if (typeof r === "object") return r;
44
+ return String(r);
45
+ } catch {
46
+ return void 0;
47
+ }
48
+ });
49
+ const roots = ref([]);
50
+ const loading = ref(false);
51
+ async function fetchTree() {
52
+ const dataSource = props.config.dataSource;
53
+ if (!dataSource) {
54
+ roots.value = [];
55
+ return;
56
+ }
57
+ const effect = Effect.gen(function* () {
58
+ let jsonOpt = Option.none();
59
+ if (dataSource.request) {
60
+ jsonOpt = yield* fetchJsonOption(yield* $cel(dataSource.request, {}));
61
+ }
62
+ const dataRaw = yield* $cel(dataSource.data, { json: jsonOpt });
63
+ return Array.isArray(dataRaw) ? dataRaw : [];
64
+ });
65
+ const program = Effect.gen(function* () {
66
+ const showMaskFiber = yield* Effect.fork(
67
+ Effect.sync(() => {
68
+ loading.value = true;
69
+ }).pipe(Effect.delay("30 millis"))
70
+ );
71
+ return yield* Effect.ensuring(effect, Fiber.interrupt(showMaskFiber));
72
+ }).pipe(
73
+ Effect.ensuring(Effect.sync(() => {
74
+ loading.value = false;
75
+ }))
76
+ );
77
+ try {
78
+ roots.value = await Effect.runPromise(Effect.provide(program, Fetch.layer));
79
+ } catch (e) {
80
+ console.warn("[shwfed-sidebar] dataSource fetch failed", e);
81
+ }
82
+ }
83
+ watch(
84
+ () => [props.config.dataSource?.request, props.config.dataSource?.data],
85
+ () => {
86
+ void fetchTree();
87
+ },
88
+ { immediate: true }
89
+ );
90
+ function nodeCtx(node) {
91
+ return { node };
92
+ }
93
+ function getKey(node) {
94
+ try {
95
+ return String(Effect.runSync($cel(props.config.nodeKey, nodeCtx(node))));
96
+ } catch (err) {
97
+ console.error("[shwfed-sidebar] failed to evaluate nodeKey:", err);
98
+ return "";
99
+ }
100
+ }
101
+ function getChildren(node) {
102
+ let raw;
103
+ try {
104
+ raw = Effect.runSync($cel(props.config.nodeChildren, nodeCtx(node)));
105
+ } catch (err) {
106
+ console.error("[shwfed-sidebar] failed to evaluate nodeChildren:", err);
107
+ return [];
108
+ }
109
+ return Array.isArray(raw) ? raw : [];
110
+ }
111
+ function getOptionalString(expr, node) {
112
+ if (!expr) return void 0;
113
+ try {
114
+ const r = Effect.runSync($cel(expr, nodeCtx(node)));
115
+ if (r == null) return void 0;
116
+ const s = String(r);
117
+ return s.length > 0 ? s : void 0;
118
+ } catch (err) {
119
+ console.error("[shwfed-sidebar] failed to evaluate node string expr:", err);
120
+ return void 0;
121
+ }
122
+ }
123
+ function nodeVisible(node) {
124
+ const expr = props.config.nodeVisible;
125
+ if (!expr) return true;
126
+ try {
127
+ return Effect.runSync($cel(expr, nodeCtx(node)));
128
+ } catch (err) {
129
+ console.error("[shwfed-sidebar] failed to evaluate nodeVisible:", err);
130
+ return false;
131
+ }
132
+ }
133
+ function nodeLabelText(node) {
134
+ const tpl = getLocalizedText(props.config.nodeLabel, locale.value) ?? "";
135
+ if (!tpl) return "";
136
+ try {
137
+ return interpolateMarkdown(tpl, $cel, { node });
138
+ } catch (err) {
139
+ console.error("[shwfed-sidebar] failed to interpolate node label:", err);
140
+ return tpl;
141
+ }
142
+ }
143
+ function buildNode(raw) {
144
+ if (!nodeVisible(raw)) return null;
145
+ const children = getChildren(raw).map(buildNode).filter((n) => n !== null);
146
+ const route = getOptionalString(props.config.nodeRoute, raw);
147
+ return {
148
+ key: getKey(raw),
149
+ label: nodeLabelText(raw),
150
+ icon: getOptionalString(props.config.nodeIcon, raw),
151
+ route,
152
+ favoritable: children.length === 0 && !!route,
153
+ children
154
+ };
155
+ }
156
+ const tree = computed(
157
+ () => roots.value.map(buildNode).filter((n) => n !== null)
158
+ );
159
+ const favoriteNodes = computed(() => {
160
+ const out = [];
161
+ const walk = (nodes) => {
162
+ for (const n of nodes) {
163
+ if (n.favoritable && favorites.isFavorite(n.key)) out.push(n);
164
+ if (n.children.length) walk(n.children);
165
+ }
166
+ };
167
+ walk(tree.value);
168
+ return out;
169
+ });
170
+ </script>
171
+
172
+ <template>
173
+ <SidebarContent
174
+ class="gap-2 py-1"
175
+ :style="styleValue"
176
+ >
177
+ <div
178
+ v-if="loading && roots.length === 0"
179
+ class="flex flex-col gap-1.5 px-2 py-1"
180
+ aria-busy="true"
181
+ >
182
+ <Skeleton class="h-6 w-2/3" />
183
+ <Skeleton class="h-6 w-1/2" />
184
+ <Skeleton class="h-6 w-3/5" />
185
+ </div>
186
+
187
+ <template v-else>
188
+ <SidebarGroup v-if="favoriteNodes.length">
189
+ <SidebarGroupLabel class="text-sm text-zinc-700">
190
+ {{ t("sidebar-favorites") }}
191
+ </SidebarGroupLabel>
192
+ <SidebarGroupContent>
193
+ <SidebarMenu>
194
+ <SidebarNode
195
+ v-for="n in favoriteNodes"
196
+ :key="`fav-${n.key}`"
197
+ :node="n"
198
+ :favorites="favorites"
199
+ />
200
+ </SidebarMenu>
201
+ </SidebarGroupContent>
202
+ </SidebarGroup>
203
+
204
+ <template
205
+ v-for="n in tree"
206
+ :key="n.key"
207
+ >
208
+ <!-- Top-level group: the group label IS the collapsible trigger -->
209
+ <SidebarGroup v-if="n.children.length">
210
+ <Collapsible
211
+ class="group/collapsible"
212
+ :open="favorites.isOpen(n.key, true)"
213
+ @update:open="(v) => favorites.setOpen(n.key, v)"
214
+ >
215
+ <SidebarGroupLabel
216
+ as-child
217
+ class="group/label w-full cursor-pointer text-sm text-zinc-700 hover:bg-zinc-100 hover:text-zinc-900"
218
+ >
219
+ <CollapsibleTrigger class="w-full">
220
+ <Icon
221
+ v-if="n.icon"
222
+ :icon="n.icon"
223
+ />
224
+ {{ n.label }}
225
+ <Icon
226
+ icon="fluent:chevron-right-20-regular"
227
+ class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
228
+ />
229
+ </CollapsibleTrigger>
230
+ </SidebarGroupLabel>
231
+ <CollapsibleContent class="overflow-hidden data-[state=open]:overflow-visible">
232
+ <SidebarGroupContent>
233
+ <SidebarMenu class="pl-4">
234
+ <SidebarNode
235
+ v-for="child in n.children"
236
+ :key="child.key"
237
+ :node="child"
238
+ :favorites="favorites"
239
+ />
240
+ </SidebarMenu>
241
+ </SidebarGroupContent>
242
+ </CollapsibleContent>
243
+ </Collapsible>
244
+ </SidebarGroup>
245
+
246
+ <!-- Top-level direct link -->
247
+ <SidebarGroup v-else>
248
+ <SidebarGroupContent>
249
+ <SidebarMenu>
250
+ <SidebarNode
251
+ :node="n"
252
+ :favorites="favorites"
253
+ />
254
+ </SidebarMenu>
255
+ </SidebarGroupContent>
256
+ </SidebarGroup>
257
+ </template>
258
+ </template>
259
+ </SidebarContent>
260
+ </template>
@@ -0,0 +1,10 @@
1
+ import type { Environment } from '../../vendor/cel-js/lib/index.js';
2
+ import type { SidebarConfigValue } from './schema.js';
3
+ type __VLS_Props = {
4
+ config: SidebarConfigValue;
5
+ configure?: (env: Environment) => void;
6
+ storageKey: string;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A normalized navigation node — the product of walking the raw `DataSource`
3
+ * payload and evaluating every per-node CEL expression once. The recursive
4
+ * renderer (`node.vue`) consumes this plain structure and never touches CEL, so
5
+ * evaluation happens a single time per node in `tree.vue` rather than once per
6
+ * recursive component instance.
7
+ *
8
+ * Group-vs-link is decided here: a node with `children` renders collapsible; a
9
+ * childless node whose `route` is non-empty renders as a navigable link (and is
10
+ * `favoritable`).
11
+ */
12
+ export interface NavNode {
13
+ key: string;
14
+ label: string;
15
+ icon?: string;
16
+ route?: string;
17
+ favoritable: boolean;
18
+ children: NavNode[];
19
+ }
File without changes
@@ -0,0 +1,18 @@
1
+ import { type ComputedRef, type Ref } from 'vue';
2
+ import type { Environment } from '../../vendor/cel-js/lib/index.js';
3
+ import { type SidebarConfigValue } from './schema.js';
4
+ export interface SidebarEditorState {
5
+ draft: Ref<SidebarConfigValue>;
6
+ validationError: Ref<string | null>;
7
+ isDirty: ComputedRef<boolean>;
8
+ configure: (env: Environment) => void;
9
+ confirm: () => void;
10
+ dismiss: () => void;
11
+ reset: () => void;
12
+ }
13
+ export interface UseSidebarEditorOptions {
14
+ configure?: (env: Environment) => void;
15
+ /** Called after a successful 确认 commits the draft to the outer model. */
16
+ onClose?: () => void;
17
+ }
18
+ export declare function useSidebarEditor(model: Ref<SidebarConfigValue>, options?: UseSidebarEditorOptions): SidebarEditorState;
@@ -0,0 +1,43 @@
1
+ import { Schema } from "effect";
2
+ import { computed, ref, watch } from "vue";
3
+ import { formatValidationError } from "../config/utils/validation-error.js";
4
+ import { SidebarConfig } from "./schema.js";
5
+ function deepClone(value) {
6
+ return JSON.parse(JSON.stringify(value));
7
+ }
8
+ export function useSidebarEditor(model, options = {}) {
9
+ const configure = options.configure ?? (() => {
10
+ });
11
+ const baseline = ref(deepClone(model.value));
12
+ const draft = ref(deepClone(baseline.value));
13
+ const isDirty = computed(
14
+ () => JSON.stringify(draft.value) !== JSON.stringify(baseline.value)
15
+ );
16
+ const validationError = ref(null);
17
+ function confirm() {
18
+ const schema = SidebarConfig(configure);
19
+ try {
20
+ const decoded = Schema.decodeUnknownSync(schema)(draft.value);
21
+ validationError.value = null;
22
+ model.value = decoded;
23
+ baseline.value = deepClone(decoded);
24
+ draft.value = deepClone(decoded);
25
+ options.onClose?.();
26
+ } catch (err) {
27
+ validationError.value = formatValidationError(err, () => void 0, draft.value);
28
+ }
29
+ }
30
+ function reset() {
31
+ baseline.value = deepClone(model.value);
32
+ draft.value = deepClone(model.value);
33
+ validationError.value = null;
34
+ }
35
+ function dismiss() {
36
+ reset();
37
+ options.onClose?.();
38
+ }
39
+ watch(draft, () => {
40
+ if (validationError.value) validationError.value = null;
41
+ }, { deep: true });
42
+ return { draft, validationError, isDirty, configure, confirm, dismiss, reset };
43
+ }
@@ -24,6 +24,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
24
24
  readonly locale: "en" | "ja" | "ko";
25
25
  readonly message: string;
26
26
  }[]] | undefined;
27
+ readonly options: string;
27
28
  readonly compatibilityDate: "2026-05-24";
28
29
  readonly grow?: boolean | undefined;
29
30
  readonly triggers?: readonly {
@@ -44,7 +45,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
44
45
  readonly readonly?: string | undefined;
45
46
  readonly request: string;
46
47
  readonly enableSorting?: boolean | undefined;
47
- readonly options: string;
48
48
  readonly optionValue: string;
49
49
  readonly optionLabel: readonly [{
50
50
  readonly locale: "zh";
@@ -79,6 +79,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
79
79
  readonly locale: "en" | "ja" | "ko";
80
80
  readonly message: string;
81
81
  }[]] | undefined;
82
+ readonly options: string;
82
83
  readonly compatibilityDate: "2026-05-24";
83
84
  readonly grow?: boolean | undefined;
84
85
  readonly triggers?: readonly {
@@ -99,7 +100,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
99
100
  readonly readonly?: string | undefined;
100
101
  readonly request: string;
101
102
  readonly enableSorting?: boolean | undefined;
102
- readonly options: string;
103
103
  readonly optionValue: string;
104
104
  readonly optionLabel: readonly [{
105
105
  readonly locale: "zh";