@shwfed/nuxt 0.7.9 → 0.7.10

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 (95) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/app.d.vue.ts +7 -56
  3. package/dist/runtime/components/app.vue +17 -404
  4. package/dist/runtime/components/app.vue.d.ts +7 -56
  5. package/dist/runtime/components/fields.d.vue.ts +154 -132
  6. package/dist/runtime/components/fields.vue +30 -295
  7. package/dist/runtime/components/fields.vue.d.ts +154 -132
  8. package/dist/runtime/components/table.d.vue.ts +63 -59
  9. package/dist/runtime/components/table.vue +52 -617
  10. package/dist/runtime/components/table.vue.d.ts +63 -59
  11. package/dist/runtime/components/ui/app/App.d.vue.ts +86 -0
  12. package/dist/runtime/components/ui/app/App.vue +414 -0
  13. package/dist/runtime/components/ui/app/App.vue.d.ts +86 -0
  14. package/dist/runtime/components/ui/checkbox/Checkbox.vue +6 -2
  15. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +30 -0
  16. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +87 -0
  17. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +30 -0
  18. package/dist/runtime/components/ui/expression-editor/index.d.ts +1 -0
  19. package/dist/runtime/components/ui/expression-editor/index.js +1 -0
  20. package/dist/runtime/components/ui/field/FieldContent.vue +1 -1
  21. package/dist/runtime/components/ui/field/FieldError.vue +2 -2
  22. package/dist/runtime/components/ui/fields/Fields.d.vue.ts +376 -0
  23. package/dist/runtime/components/ui/fields/Fields.vue +441 -0
  24. package/dist/runtime/components/ui/fields/Fields.vue.d.ts +376 -0
  25. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +163 -0
  26. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +363 -0
  27. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +163 -0
  28. package/dist/runtime/components/ui/input/Input.d.vue.ts +1 -0
  29. package/dist/runtime/components/ui/input/Input.vue +2 -0
  30. package/dist/runtime/components/ui/input/Input.vue.d.ts +1 -0
  31. package/dist/runtime/components/ui/input-group/InputGroupAddon.vue +4 -1
  32. package/dist/runtime/components/ui/input-group/InputGroupCombobox.d.vue.ts +8 -3
  33. package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue +8 -3
  34. package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue.d.ts +8 -3
  35. package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.d.vue.ts +8 -1
  36. package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue +10 -1
  37. package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue.d.ts +8 -1
  38. package/dist/runtime/components/ui/input-group/InputGroupNumberField.d.vue.ts +5 -2
  39. package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue +9 -3
  40. package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue.d.ts +5 -2
  41. package/dist/runtime/components/ui/input-group/index.js +1 -1
  42. package/dist/runtime/components/ui/locale/Locale.d.vue.ts +20 -0
  43. package/dist/runtime/components/ui/locale/Locale.vue +291 -0
  44. package/dist/runtime/components/ui/locale/Locale.vue.d.ts +20 -0
  45. package/dist/runtime/components/ui/locale/index.d.ts +1 -0
  46. package/dist/runtime/components/ui/locale/index.js +1 -0
  47. package/dist/runtime/components/ui/native-select/NativeSelectOption.d.vue.ts +1 -0
  48. package/dist/runtime/components/ui/native-select/NativeSelectOption.vue +4 -1
  49. package/dist/runtime/components/ui/native-select/NativeSelectOption.vue.d.ts +1 -0
  50. package/dist/runtime/components/ui/number-field/NumberFieldInput.vue +1 -1
  51. package/dist/runtime/components/ui/switch/Switch.vue +1 -1
  52. package/dist/runtime/components/ui/table/Table.d.vue.ts +81 -0
  53. package/dist/runtime/components/ui/table/Table.vue +792 -0
  54. package/dist/runtime/components/ui/table/Table.vue.d.ts +81 -0
  55. package/dist/runtime/components/ui/table/schema.d.ts +48 -0
  56. package/dist/runtime/components/ui/table/schema.js +126 -0
  57. package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.d.vue.ts +62 -0
  58. package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +2233 -0
  59. package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue.d.ts +62 -0
  60. package/dist/runtime/components/ui/table-configurator/menu.d.ts +37 -0
  61. package/dist/runtime/components/ui/table-configurator/menu.js +227 -0
  62. package/dist/runtime/components/ui/tabs/Tabs.d.vue.ts +24 -0
  63. package/dist/runtime/components/ui/tabs/Tabs.vue +30 -0
  64. package/dist/runtime/components/ui/tabs/Tabs.vue.d.ts +24 -0
  65. package/dist/runtime/components/ui/tabs/TabsContent.d.vue.ts +18 -0
  66. package/dist/runtime/components/ui/tabs/TabsContent.vue +23 -0
  67. package/dist/runtime/components/ui/tabs/TabsContent.vue.d.ts +18 -0
  68. package/dist/runtime/components/ui/tabs/TabsList.d.vue.ts +18 -0
  69. package/dist/runtime/components/ui/tabs/TabsList.vue +25 -0
  70. package/dist/runtime/components/ui/tabs/TabsList.vue.d.ts +18 -0
  71. package/dist/runtime/components/ui/tabs/TabsTrigger.d.vue.ts +18 -0
  72. package/dist/runtime/components/ui/tabs/TabsTrigger.vue +27 -0
  73. package/dist/runtime/components/ui/tabs/TabsTrigger.vue.d.ts +18 -0
  74. package/dist/runtime/components/ui/tabs/index.d.ts +4 -0
  75. package/dist/runtime/components/ui/tabs/index.js +4 -0
  76. package/dist/runtime/components/ui/textarea/Textarea.d.vue.ts +1 -0
  77. package/dist/runtime/components/ui/textarea/Textarea.vue +3 -1
  78. package/dist/runtime/components/ui/textarea/Textarea.vue.d.ts +1 -0
  79. package/dist/runtime/components/ui/toggle/Toggle.d.vue.ts +34 -0
  80. package/dist/runtime/components/ui/toggle/Toggle.vue +32 -0
  81. package/dist/runtime/components/ui/toggle/Toggle.vue.d.ts +34 -0
  82. package/dist/runtime/components/ui/toggle/index.d.ts +7 -0
  83. package/dist/runtime/components/ui/toggle/index.js +22 -0
  84. package/dist/runtime/composables/useTableRenderers.d.ts +2 -1
  85. package/dist/runtime/composables/useTableRenderers.js +2 -1
  86. package/dist/runtime/style.css +1 -1
  87. package/dist/runtime/table-renderers/builtins.js +213 -98
  88. package/dist/runtime/table-renderers/registry.d.ts +1 -0
  89. package/dist/runtime/table-renderers/registry.js +3 -0
  90. package/dist/runtime/utils/coders.d.ts +27 -2
  91. package/dist/runtime/utils/coders.js +27 -2
  92. package/package.json +3 -1
  93. /package/dist/runtime/components/{logo.d.vue.ts → ui/logo/Logo.d.vue.ts} +0 -0
  94. /package/dist/runtime/components/{logo.vue → ui/logo/Logo.vue} +0 -0
  95. /package/dist/runtime/components/{logo.vue.d.ts → ui/logo/Logo.vue.d.ts} +0 -0
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
3
  "configKey": "shwfed",
4
- "version": "0.7.9",
4
+ "version": "0.7.10",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1,13 +1,14 @@
1
1
  import type { Scope } from 'effect';
2
- import { Effect } from 'effect';
3
- import { type Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
2
+ import type { Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
3
+ type CommandEffect = import('effect').Effect.Effect<void>;
4
+ type DslEffect = import('effect').Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
4
5
  type ProfileCommandInputItem = Readonly<{
5
6
  id: string | number;
6
7
  title: string;
7
8
  icon?: string;
8
9
  hidden?: boolean;
9
10
  disabled?: boolean;
10
- effect?: Effect.Effect<void>;
11
+ effect?: CommandEffect;
11
12
  keywords?: Array<string>;
12
13
  }>;
13
14
  type ProfileCommandInputGroup = Readonly<{
@@ -17,63 +18,13 @@ type ProfileCommandInputGroup = Readonly<{
17
18
  children: Array<ProfileCommandInputItem>;
18
19
  }>;
19
20
  type __VLS_Props = {
20
- /**
21
- * 应用的全局 DSL(CEL)执行上下文。传入一个 Effect,在应用启动时运行一次;
22
- * 其解析结果(一个 record)会被永久设为全局上下文,供后续所有 CEL 求值(如 `$dsl.evaluate`)使用。
23
- *
24
- * 应用会等待该 effect 解析完成后才渲染;在整个应用生命周期内只会执行一次。
25
- * 若未传入 `dsl`,全局上下文将被设为 `{}`。
26
- *
27
- * 适用于需要在首屏前就确定的、会话级稳定数据,例如:当前用户信息、用户所属组、
28
- * 用户菜单列表、用户权限(或权限模块)等。此处加载的数据在整个应用内可通过 CEL 访问
29
- *(如 `user`、`menu`、`permissions`)。强调「会话级」稳定即可,不必永久不变。
30
- *
31
- * 重要约束:
32
- * - 加载时间至关重要:在该 effect 解析前,整应用不会加载,用户会看到白屏,因此必须保持非常快
33
- * (如减少请求、缓存或并行拉取)。
34
- * - 这是有意为之的权衡:菜单、用户等关键数据必须在此加载,否则可能出现空菜单、无用户等异常表现。
35
- * - 该 effect 不得抛错(类型为 `never`):任何失败(如网络)应在 effect 内部处理,
36
- * 返回空对象或安全默认值,保证应用仍能正常启动并得到一致的全局上下文。
37
- *
38
- * 返回的 `Record<string, unknown>` 会在通过 `$dsl.evaluate` 求值 CEL 表达式时,
39
- * 与运行时上下文(如 `now`、`today` 及调用处传入的 context)合并使用。
40
- *
41
- * @example 最小示例:返回静态对象,键可在 CEL 中直接使用
42
- * dsl: Effect.succeed({ hello: 'world' })
43
- * // 之后在模板或逻辑中:$dsl.evaluate`hello`() 得到 'world'
44
- *
45
- * @example 实际场景:拉取会话数据(用户、菜单、权限)并返回,供 CEL 使用
46
- * dsl: Effect.gen(function* () {
47
- * const [user, menu, permissions] = yield* Effect.all([
48
- * fetchUser().pipe(Effect.catchAll(() => Effect.succeed({ name: '', id: '' }))),
49
- * fetchMenu().pipe(Effect.catchAll(() => Effect.succeed([]))),
50
- * fetchPermissions().pipe(Effect.catchAll(() => Effect.succeed({ canEdit: false }))),
51
- * ])
52
- * return { user, menu, permissions }
53
- * })
54
- * // CEL 中可访问 user.name、permissions.canEdit 等;失败时在 effect 内兜底,不抛错
55
- */
56
- dsl?: Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
57
- /**
58
- * Sidebar config.
59
- *
60
- * `menus` is structured navigation data.
61
- */
21
+ dsl?: DslEffect;
62
22
  sidebar?: SidebarType;
63
- /**
64
- * Profile command config.
65
- *
66
- * Structured command entries for profile menu and command palette.
67
- */
68
23
  commands?: Array<ProfileCommandInputItem | ProfileCommandInputGroup>;
69
24
  };
70
- declare var __VLS_108: {}, __VLS_124: {}, __VLS_347: {};
25
+ declare var __VLS_10: string, __VLS_11: any;
71
26
  type __VLS_Slots = {} & {
72
- menu?: (props: typeof __VLS_108) => any;
73
- } & {
74
- profile?: (props: typeof __VLS_124) => any;
75
- } & {
76
- default?: (props: typeof __VLS_347) => any;
27
+ [K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
77
28
  };
78
29
  declare const __VLS_base: 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>;
79
30
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,414 +1,27 @@
1
1
  <script setup>
2
- import { useHead, useNuxtApp, useRuntimeConfig } from "#app";
3
- import { reactive } from "vue";
4
- import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "./ui/command";
5
- import { TooltipProvider } from "./ui/tooltip";
6
- import { Toaster } from "vue-sonner";
7
- import { useMagicKeys, useTimeoutFn, whenever } from "@vueuse/core";
8
- import { useI18n } from "vue-i18n";
9
- import { Icon } from "@iconify/vue";
10
- import { Effect } from "effect";
11
- import { setGlobalDslContext } from "../plugins/cel/context";
12
- import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from "./ui/sidebar";
13
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
14
- import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "./ui/dropdown-menu";
15
- import Logo from "./logo.vue";
16
- import { useFavorite } from "../composables/useFavorite";
17
- import { useNavigationTabs } from "../composables/useNavigationTabs";
18
- const {
19
- public: {
20
- shwfed
21
- }
22
- } = useRuntimeConfig();
23
- const { $dsl } = useNuxtApp();
24
- const { t } = useI18n();
2
+ import UiApp from "./ui/app/App.vue";
3
+ defineOptions({
4
+ inheritAttrs: false
5
+ });
25
6
  const props = defineProps({
26
7
  dsl: { type: null, required: false },
27
8
  sidebar: { type: Array, required: false },
28
9
  commands: { type: Array, required: false }
29
10
  });
30
- const { active, tabs, nameOf, close } = useNavigationTabs(() => props.sidebar ?? []);
31
- const ui = reactive({
32
- isCommandPaletteOpen: false,
33
- isProfileDropdownOpen: false
34
- });
35
- useHead({
36
- title: () => active.value && nameOf(active.value),
37
- bodyAttrs: {
38
- style: {
39
- "--primary": "#2DA8BC",
40
- "--workspace": "#FFFFFF"
41
- }
42
- }
43
- });
44
- const { start: startProfileCloseTimer, stop: stopProfileCloseTimer } = useTimeoutFn(() => {
45
- ui.isProfileDropdownOpen = false;
46
- }, 150, { immediate: false });
47
- const { meta_k } = useMagicKeys();
48
- whenever(() => meta_k?.value, () => ui.isCommandPaletteOpen = !ui.isCommandPaletteOpen);
49
- setGlobalDslContext(await props.dsl?.pipe(Effect.scoped).pipe(Effect.runPromise) ?? {});
50
- const {
51
- isFavorited: isNavigationFavorited,
52
- canBeFavorited: canBeNavigationFavorited,
53
- toggle: toggleNavigationFavorite,
54
- withFavorites: navigations
55
- } = useFavorite("navigation", () => props.sidebar ?? []);
56
- const logout = () => {
57
- if (shwfed.api.logout) {
58
- window.location.href = shwfed.api.logout;
59
- }
60
- return Effect.void;
61
- };
62
11
  </script>
63
12
 
64
13
  <template>
65
- <TooltipProvider>
66
- <ClientOnly>
67
- <Toaster />
68
- <CommandDialog
69
- v-model:open="ui.isCommandPaletteOpen"
70
- >
71
- <CommandInput :placeholder="t('search')" />
72
- <CommandList>
73
- <CommandEmpty as-child>
74
- <section class="h-32 flex flex-col text-lg items-center justify-center gap-2 select-none">
75
- <Icon
76
- icon="fluent:app-recent-20-regular"
77
- class="text-zinc-400 text-2xl!"
78
- />
79
- <p class="text-zinc-500">
80
- {{ t("command-palette-empty") }}
81
- </p>
82
- </section>
83
- </CommandEmpty>
84
-
85
- <template
86
- v-for="command in props.commands"
87
- :key="command.id"
88
- >
89
- <CommandGroup
90
- v-if="'children' in command"
91
- :heading="command.title"
92
- >
93
- <CommandItem
94
- v-for="child in command.children.filter((child2) => !child2.hidden)"
95
- :key="child.id"
96
- :value="child.id"
97
- :disabled="child.disabled"
98
- @select="child.effect?.pipe(Effect.tap(() => {
99
- ui.isCommandPaletteOpen = false;
100
- })).pipe(Effect.runPromise)"
101
- >
102
- <span>{{ child.title }}</span>
103
- <span class="sr-only">
104
- {{ child.keywords?.join(" ") }}
105
- </span>
106
- </CommandItem>
107
- </CommandGroup>
108
- <CommandGroup v-else>
109
- <CommandItem
110
- :value="command.id"
111
- :disabled="command.disabled"
112
- @select="command.effect?.pipe(Effect.tap(() => {
113
- ui.isCommandPaletteOpen = false;
114
- })).pipe(Effect.runPromise)"
115
- >
116
- <span>{{ command.title }}</span>
117
- <span class="sr-only">
118
- {{ command.keywords?.join(" ") }}
119
- </span>
120
- </CommandItem>
121
- </CommandGroup>
122
- </template>
123
-
124
- <template
125
- v-for="navigation in props.sidebar"
126
- :key="navigation.id"
127
- >
128
- <CommandGroup
129
- v-if="'children' in navigation"
130
- :heading="navigation.title"
131
- >
132
- <CommandItem
133
- v-for="menu in navigation.children"
134
- :key="menu.id"
135
- :value="menu.id"
136
- @select="$router.replace(menu.route)"
137
- >
138
- <span>{{ menu.title }}</span>
139
- </CommandItem>
140
- </CommandGroup>
141
- <CommandGroup v-else>
142
- <CommandItem
143
- :value="navigation.id"
144
- @select="$router.replace(navigation.route)"
145
- >
146
- <span>{{ navigation.title }}</span>
147
- </CommandItem>
148
- </CommandGroup>
149
- </template>
150
- </CommandList>
151
- </CommandDialog>
152
- </ClientOnly>
153
-
154
- <main class="h-screen w-screen flex flex-col overflow-hidden">
155
- <header class="bg-(--primary) h-12 w-full flex items-center justify-between px-4 gap-4">
156
- <Logo />
157
- <slot name="menu" />
158
- <span class="flex-1" />
159
- <DropdownMenu
160
- v-model:open="ui.isProfileDropdownOpen"
161
- :modal="false"
162
- @update:open="ui.isProfileDropdownOpen = $event"
163
- >
164
- <DropdownMenuTrigger as-child>
165
- <button
166
- type="button"
167
- class="text-white text-sm cursor-pointer appearance-none outline-none py-2 px-3 rounded hover:bg-white/10"
168
- @pointerenter="stopProfileCloseTimer();
169
- ui.isProfileDropdownOpen = true"
170
- @pointerleave="stopProfileCloseTimer();
171
- startProfileCloseTimer()"
172
- @click.prevent="stopProfileCloseTimer();
173
- ui.isProfileDropdownOpen = true"
174
- >
175
- <slot name="profile" />
176
- </button>
177
- </DropdownMenuTrigger>
178
- <DropdownMenuContent
179
- class="min-w-56"
180
- align="end"
181
- @pointerenter="stopProfileCloseTimer()"
182
- @pointerleave="stopProfileCloseTimer();
183
- startProfileCloseTimer()"
184
- >
185
- <template
186
- v-for="command in props.commands"
187
- :key="command.id"
188
- >
189
- <template v-if="'children' in command && command.children.some((child2) => !child2.hidden)">
190
- <DropdownMenuLabel>
191
- {{ command.title }}
192
- </DropdownMenuLabel>
193
- <DropdownMenuGroup>
194
- <template
195
- v-for="child in command.children"
196
- :key="child.id"
197
- >
198
- <DropdownMenuItem
199
- v-if="!child.hidden"
200
- :disabled="child.disabled"
201
- @select="child.effect?.pipe(Effect.runPromise)"
202
- >
203
- <Icon
204
- v-if="child.icon"
205
- :icon="child.icon"
206
- />
207
- {{ child.title }}
208
- </DropdownMenuItem>
209
- </template>
210
- </DropdownMenuGroup>
211
- <DropdownMenuSeparator />
212
- </template>
213
- <DropdownMenuItem
214
- v-else-if="!('children' in command) && !command.hidden"
215
- :disabled="command.disabled"
216
- @select="command.effect?.pipe(Effect.runPromise)"
217
- >
218
- <Icon
219
- v-if="command.icon"
220
- :icon="command.icon"
221
- />
222
- {{ command.title }}
223
- </DropdownMenuItem>
224
- </template>
225
- <DropdownMenuItem
226
- @select="logout"
227
- >
228
- <Icon
229
- icon="fluent:sign-out-20-regular"
230
- />
231
- {{ t("logout") }}
232
- </DropdownMenuItem>
233
- <DropdownMenuSeparator v-if="props.commands?.some((command2) => 'children' in command2 ? command2.children.some((child2) => !child2.hidden) : !command2.hidden)" />
234
- <DropdownMenuItem disabled>
235
- <Icon icon="fluent:history-20-regular" />
236
- {{ t("build") }}
237
- <code class="uppercase">{{ $dsl.evaluate`has(git.commit) && git.commit.size() > 0 ? git.commit.slice(0, 7) : 'development'`() }}</code>
238
- </DropdownMenuItem>
239
- </DropdownMenuContent>
240
- </DropdownMenu>
241
- </header>
242
- <main class="flex-1 flex w-full overflow-hidden">
243
- <SidebarProvider
244
- default-open
245
- class="w-fit"
246
- :style="{
247
- '--sidebar-width': '13rem'
248
- }"
249
- >
250
- <Sidebar
251
- collapsible="icon"
252
- class="sticky h-full"
253
- >
254
- <SidebarContent class="gap-2 py-1">
255
- <template
256
- v-for="group in navigations"
257
- :key="group.id"
258
- >
259
- <Collapsible
260
- v-if="'children' in group"
261
- class="group/collapsible"
262
- >
263
- <SidebarGroup>
264
- <SidebarGroupLabel
265
- as-child
266
- class="group/label text-sm text-zinc-700 hover:bg-zinc-100 hover:text-zinc-900"
267
- >
268
- <CollapsibleTrigger>
269
- <Icon
270
- v-if="group.icon"
271
- :icon="group.icon"
272
- />
273
- {{ group.title }}
274
- <Icon
275
- icon="fluent:chevron-right-20-regular"
276
- class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
277
- />
278
- </CollapsibleTrigger>
279
- </SidebarGroupLabel>
280
- <CollapsibleContent class="overflow-hidden data-[state=open]:overflow-visible data-[state=open]:h-auto">
281
- <SidebarGroupContent>
282
- <SidebarMenu class="pl-4">
283
- <SidebarMenuItem
284
- v-for="menu in group.children"
285
- :key="menu.id"
286
- >
287
- <SidebarMenuButton
288
- v-if="'route' in menu"
289
- as-child
290
- :is-active="active === menu.route"
291
- >
292
- <button
293
- type="button"
294
- @click="active = menu.route"
295
- >
296
- <Icon
297
- v-if="menu.icon"
298
- :icon="menu.icon"
299
- />
300
- {{ menu.title }}
301
- </button>
302
- </SidebarMenuButton>
303
- <SidebarMenuAction
304
- v-if="canBeNavigationFavorited(menu, group.id)"
305
- :show-on-hover="!isNavigationFavorited(menu, group.id)"
306
- class="text-yellow-500!"
307
- @click.stop="toggleNavigationFavorite(menu, group.id)"
308
- >
309
- <Icon
310
- :icon="isNavigationFavorited(menu, group.id) ? 'fluent:star-20-filled' : 'fluent:star-20-regular'"
311
- />
312
- </SidebarMenuAction>
313
- </SidebarMenuItem>
314
- </SidebarMenu>
315
- </SidebarGroupContent>
316
- </CollapsibleContent>
317
- </SidebarGroup>
318
- </Collapsible>
319
- <template v-else-if="'route' in group">
320
- <SidebarGroup>
321
- <SidebarGroupContent>
322
- <SidebarMenu>
323
- <SidebarMenuItem>
324
- <SidebarMenuButton
325
- as-child
326
- :is-active="active === group.route"
327
- >
328
- <button
329
- type="button"
330
- @click="active = group.route"
331
- >
332
- <Icon
333
- v-if="group.icon"
334
- :icon="group.icon"
335
- />
336
- {{ group.title }}
337
- </button>
338
- </SidebarMenuButton>
339
- </SidebarMenuItem>
340
- </SidebarMenu>
341
- </SidebarGroupContent>
342
- </SidebarGroup>
343
- </template>
344
- </template>
345
- </SidebarContent>
346
- </Sidebar>
347
- </SidebarProvider>
348
- <main class="flex-1 flex flex-col bg-zinc-100 overflow-hidden">
349
- <nav class="bg-white p-1 border-b border-zinc-100 h-10 flex items-center justify-start gap-1 overflow-x-auto min-w-0">
350
- <div
351
- v-for="tab in tabs"
352
- :key="tab"
353
- :class="[
354
- 'h-8 max-w-60 border rounded px-2 text-sm flex items-center gap-2 shrink-0 min-w-0 transition-colors',
355
- active === tab ? 'text-(--primary) border-(--primary) bg-cyan-50' : 'text-zinc-600 border-zinc-200 hover:text-zinc-900 hover:border-zinc-300'
356
- ]"
357
- >
358
- <button
359
- type="button"
360
- class="truncate cursor-pointer"
361
- @click="active = tab"
362
- >
363
- {{ nameOf(tab) }}
364
- </button>
365
- <button
366
- v-if="tabs.size !== 1"
367
- type="button"
368
- class="cursor-pointer text-zinc-500 hover:text-zinc-900"
369
- @click.stop="close(tab)"
370
- >
371
- <Icon icon="fluent:dismiss-20-regular" />
372
- </button>
373
- </div>
374
- </nav>
375
- <main class="m-2 flex-1 p-1 bg-(--workspace) border border-zinc-100 rounded overflow-hidden">
376
- <slot />
377
- </main>
378
- </main>
379
- </main>
380
- </main>
381
- </TooltipProvider>
14
+ <UiApp
15
+ v-bind="{ ...props, ...$attrs }"
16
+ >
17
+ <template
18
+ v-for="(_, slotName) in $slots"
19
+ #[slotName]="slotProps"
20
+ >
21
+ <slot
22
+ :name="slotName"
23
+ v-bind="slotProps ?? {}"
24
+ />
25
+ </template>
26
+ </UiApp>
382
27
  </template>
383
-
384
- <i18n lang="json">
385
- {
386
- "zh": {
387
- "command-palette-empty": "无搜索结果",
388
- "search": "搜索",
389
- "logout": "退出登录",
390
- "build": "构建",
391
- "navigation": "导航",
392
- "profile": "个人信息",
393
- "favorites": "收藏"
394
- },
395
- "ja": {
396
- "command-palette-empty": "結果はありません",
397
- "search": "検索",
398
- "logout": "ログアウト",
399
- "build": "ビルド",
400
- "navigation": "ナビゲーション",
401
- "profile": "プロフィール",
402
- "favorites": "お気に入り"
403
- },
404
- "en": {
405
- "command-palette-empty": "No results",
406
- "search": "Search",
407
- "logout": "Logout",
408
- "build": "Build",
409
- "navigation": "Navigation",
410
- "profile": "Profile",
411
- "favorites": "Favorites"
412
- }
413
- }
414
- </i18n>
@@ -1,13 +1,14 @@
1
1
  import type { Scope } from 'effect';
2
- import { Effect } from 'effect';
3
- import { type Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
2
+ import type { Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
3
+ type CommandEffect = import('effect').Effect.Effect<void>;
4
+ type DslEffect = import('effect').Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
4
5
  type ProfileCommandInputItem = Readonly<{
5
6
  id: string | number;
6
7
  title: string;
7
8
  icon?: string;
8
9
  hidden?: boolean;
9
10
  disabled?: boolean;
10
- effect?: Effect.Effect<void>;
11
+ effect?: CommandEffect;
11
12
  keywords?: Array<string>;
12
13
  }>;
13
14
  type ProfileCommandInputGroup = Readonly<{
@@ -17,63 +18,13 @@ type ProfileCommandInputGroup = Readonly<{
17
18
  children: Array<ProfileCommandInputItem>;
18
19
  }>;
19
20
  type __VLS_Props = {
20
- /**
21
- * 应用的全局 DSL(CEL)执行上下文。传入一个 Effect,在应用启动时运行一次;
22
- * 其解析结果(一个 record)会被永久设为全局上下文,供后续所有 CEL 求值(如 `$dsl.evaluate`)使用。
23
- *
24
- * 应用会等待该 effect 解析完成后才渲染;在整个应用生命周期内只会执行一次。
25
- * 若未传入 `dsl`,全局上下文将被设为 `{}`。
26
- *
27
- * 适用于需要在首屏前就确定的、会话级稳定数据,例如:当前用户信息、用户所属组、
28
- * 用户菜单列表、用户权限(或权限模块)等。此处加载的数据在整个应用内可通过 CEL 访问
29
- *(如 `user`、`menu`、`permissions`)。强调「会话级」稳定即可,不必永久不变。
30
- *
31
- * 重要约束:
32
- * - 加载时间至关重要:在该 effect 解析前,整应用不会加载,用户会看到白屏,因此必须保持非常快
33
- * (如减少请求、缓存或并行拉取)。
34
- * - 这是有意为之的权衡:菜单、用户等关键数据必须在此加载,否则可能出现空菜单、无用户等异常表现。
35
- * - 该 effect 不得抛错(类型为 `never`):任何失败(如网络)应在 effect 内部处理,
36
- * 返回空对象或安全默认值,保证应用仍能正常启动并得到一致的全局上下文。
37
- *
38
- * 返回的 `Record<string, unknown>` 会在通过 `$dsl.evaluate` 求值 CEL 表达式时,
39
- * 与运行时上下文(如 `now`、`today` 及调用处传入的 context)合并使用。
40
- *
41
- * @example 最小示例:返回静态对象,键可在 CEL 中直接使用
42
- * dsl: Effect.succeed({ hello: 'world' })
43
- * // 之后在模板或逻辑中:$dsl.evaluate`hello`() 得到 'world'
44
- *
45
- * @example 实际场景:拉取会话数据(用户、菜单、权限)并返回,供 CEL 使用
46
- * dsl: Effect.gen(function* () {
47
- * const [user, menu, permissions] = yield* Effect.all([
48
- * fetchUser().pipe(Effect.catchAll(() => Effect.succeed({ name: '', id: '' }))),
49
- * fetchMenu().pipe(Effect.catchAll(() => Effect.succeed([]))),
50
- * fetchPermissions().pipe(Effect.catchAll(() => Effect.succeed({ canEdit: false }))),
51
- * ])
52
- * return { user, menu, permissions }
53
- * })
54
- * // CEL 中可访问 user.name、permissions.canEdit 等;失败时在 effect 内兜底,不抛错
55
- */
56
- dsl?: Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
57
- /**
58
- * Sidebar config.
59
- *
60
- * `menus` is structured navigation data.
61
- */
21
+ dsl?: DslEffect;
62
22
  sidebar?: SidebarType;
63
- /**
64
- * Profile command config.
65
- *
66
- * Structured command entries for profile menu and command palette.
67
- */
68
23
  commands?: Array<ProfileCommandInputItem | ProfileCommandInputGroup>;
69
24
  };
70
- declare var __VLS_108: {}, __VLS_124: {}, __VLS_347: {};
25
+ declare var __VLS_10: string, __VLS_11: any;
71
26
  type __VLS_Slots = {} & {
72
- menu?: (props: typeof __VLS_108) => any;
73
- } & {
74
- profile?: (props: typeof __VLS_124) => any;
75
- } & {
76
- default?: (props: typeof __VLS_347) => any;
27
+ [K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
77
28
  };
78
29
  declare const __VLS_base: 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>;
79
30
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;