@openviking/react 0.0.0-stage → 0.1.1

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 (104) hide show
  1. package/README.md +82 -2
  2. package/dist/3~0.js +8 -0
  3. package/dist/a~0.js +21 -0
  4. package/dist/client/client.d.ts +12 -0
  5. package/dist/client/errors.d.ts +30 -0
  6. package/dist/client/index.d.ts +4 -0
  7. package/dist/client/options.d.ts +101 -0
  8. package/dist/client/provider.d.ts +52 -0
  9. package/dist/client/types.d.ts +160 -0
  10. package/dist/client.js +239 -0
  11. package/dist/components/agent-connect/agent-connect.d.ts +4 -0
  12. package/dist/components/agent-connect/index.d.ts +1 -0
  13. package/dist/components/agent-connect/style.css +16 -0
  14. package/dist/components/agent-connect/style.js +0 -0
  15. package/dist/components/agent-connect/types.d.ts +175 -0
  16. package/dist/components/agent-connect.js +1 -0
  17. package/dist/components/file-preview/file-preview-body.d.ts +15 -0
  18. package/dist/components/file-preview/file-preview-header.d.ts +16 -0
  19. package/dist/components/file-preview/file-preview-icons.d.ts +2 -0
  20. package/dist/components/file-preview/file-preview-labels.d.ts +5 -0
  21. package/dist/components/file-preview/file-preview-operations.d.ts +1 -0
  22. package/dist/components/file-preview/file-preview.d.ts +17 -0
  23. package/dist/components/file-preview/index.d.ts +1 -0
  24. package/dist/components/file-preview/style.css +285 -0
  25. package/dist/components/file-preview/style.js +0 -0
  26. package/dist/components/file-preview/types.d.ts +435 -0
  27. package/dist/components/file-preview/use-file-preview-editing.d.ts +13 -0
  28. package/dist/components/file-preview.js +442 -0
  29. package/dist/components/file-tree/file-tree-body.d.ts +14 -0
  30. package/dist/components/file-tree/file-tree-dom.d.ts +4 -0
  31. package/dist/components/file-tree/file-tree-entry-row.d.ts +6 -0
  32. package/dist/components/file-tree/file-tree-icons.d.ts +8 -0
  33. package/dist/components/file-tree/file-tree-keyboard.d.ts +10 -0
  34. package/dist/components/file-tree/file-tree-labels.d.ts +5 -0
  35. package/dist/components/file-tree/file-tree-row-context.d.ts +24 -0
  36. package/dist/components/file-tree/file-tree-rows.d.ts +12 -0
  37. package/dist/components/file-tree/file-tree-tooltip.d.ts +11 -0
  38. package/dist/components/file-tree/file-tree.d.ts +4 -0
  39. package/dist/components/file-tree/index.d.ts +1 -0
  40. package/dist/components/file-tree/style.css +244 -0
  41. package/dist/components/file-tree/style.js +0 -0
  42. package/dist/components/file-tree/types.d.ts +406 -0
  43. package/dist/components/file-tree/use-file-tree-actions.d.ts +10 -0
  44. package/dist/components/file-tree.js +1 -0
  45. package/dist/components/scope-switcher/index.d.ts +1 -0
  46. package/dist/components/scope-switcher/scope-keyboard.d.ts +2 -0
  47. package/dist/components/scope-switcher/scope-panel.d.ts +5 -0
  48. package/dist/components/scope-switcher/scope-selector.d.ts +2 -0
  49. package/dist/components/scope-switcher/scope-switcher.d.ts +6 -0
  50. package/dist/components/scope-switcher/style.css +248 -0
  51. package/dist/components/scope-switcher/style.js +0 -0
  52. package/dist/components/scope-switcher/types.d.ts +367 -0
  53. package/dist/components/scope-switcher.js +3 -0
  54. package/dist/components/style.css +811 -0
  55. package/dist/components/style.js +0 -0
  56. package/dist/components/tui/index.d.ts +1 -0
  57. package/dist/components/tui/style.css +16 -0
  58. package/dist/components/tui/style.js +0 -0
  59. package/dist/components/tui/tui.d.ts +3 -0
  60. package/dist/components/tui/types.d.ts +485 -0
  61. package/dist/components/tui.js +1 -0
  62. package/dist/f~0.js +284 -0
  63. package/dist/icons/index.d.ts +23 -0
  64. package/dist/icons.js +25 -0
  65. package/dist/index.d.ts +16 -0
  66. package/dist/index.js +8 -0
  67. package/dist/i~0.js +21 -0
  68. package/dist/lk~0.js +220 -0
  69. package/dist/l~0.js +44 -0
  70. package/dist/modules/console/console.d.ts +4 -0
  71. package/dist/modules/console/index.d.ts +1 -0
  72. package/dist/modules/console/style.css +29 -0
  73. package/dist/modules/console/style.js +0 -0
  74. package/dist/modules/console/types.d.ts +101 -0
  75. package/dist/modules/console.js +1 -0
  76. package/dist/modules/file-browser/file-browser-toolbar-button.d.ts +10 -0
  77. package/dist/modules/file-browser/file-browser.d.ts +6 -0
  78. package/dist/modules/file-browser/index.d.ts +1 -0
  79. package/dist/modules/file-browser/style.css +75 -0
  80. package/dist/modules/file-browser/style.js +0 -0
  81. package/dist/modules/file-browser/types.d.ts +101 -0
  82. package/dist/modules/file-browser.js +1 -0
  83. package/dist/modules/scope-cascade/controller.d.ts +6 -0
  84. package/dist/modules/scope-cascade/index.d.ts +3 -0
  85. package/dist/modules/scope-cascade/scope-cascade.d.ts +3 -0
  86. package/dist/modules/scope-cascade/style.css +250 -0
  87. package/dist/modules/scope-cascade/style.js +0 -0
  88. package/dist/modules/scope-cascade/types.d.ts +39 -0
  89. package/dist/modules/scope-cascade/use-scope-cascade.d.ts +5 -0
  90. package/dist/modules/scope-cascade.js +1 -0
  91. package/dist/modules/style.css +385 -0
  92. package/dist/modules/style.js +0 -0
  93. package/dist/modules/task-center/columns.d.ts +129 -0
  94. package/dist/modules/task-center/index.d.ts +1 -0
  95. package/dist/modules/task-center/style.css +29 -0
  96. package/dist/modules/task-center/style.js +0 -0
  97. package/dist/modules/task-center/task-center.d.ts +4 -0
  98. package/dist/modules/task-center/types.d.ts +168 -0
  99. package/dist/modules/task-center.js +1 -0
  100. package/dist/n~0.js +499 -0
  101. package/dist/style.css +1198 -0
  102. package/dist/style.js +0 -0
  103. package/dist/u~0.js +8 -0
  104. package/package.json +103 -4
@@ -0,0 +1,29 @@
1
+ .ov-module-placeholder {
2
+ box-sizing: border-box;
3
+ border: 1px dashed var(--ov-ui-border, currentColor);
4
+ border-radius: var(--ov-ui-radius, 6px);
5
+ min-width: 0;
6
+ min-height: 160px;
7
+ color: inherit;
8
+ font: inherit;
9
+ text-align: center;
10
+ overflow-wrap: anywhere;
11
+ flex-direction: column;
12
+ justify-content: center;
13
+ align-items: center;
14
+ gap: 8px;
15
+ padding: 24px;
16
+ display: flex;
17
+ }
18
+
19
+ .ov-module-placeholder__title {
20
+ margin: 0;
21
+ }
22
+
23
+ .ov-module-placeholder__description {
24
+ color: var(--ov-ui-text-muted, color-mix(in srgb, currentColor 75%, transparent));
25
+ margin: 0;
26
+ }
27
+
28
+
29
+
File without changes
@@ -0,0 +1,101 @@
1
+ import type { OvRequestOptions } from '../../client/index.js';
2
+ import type { HTMLAttributes, ReactNode } from 'react';
3
+ /**
4
+ * 面板模式。对话与命令共用同一面板,切换时各自保留状态。
5
+ * Panel mode. Conversation and commands share one panel, each keeping its own
6
+ * state when the mode switches.
7
+ */
8
+ export type ConsoleMode = 'conversation' | 'command';
9
+ /** 交互面板模块的插槽。Slots of the console module. */
10
+ export interface ConsoleSlots {
11
+ /**
12
+ * 面板顶部工具栏。模式切换由模块渲染,这里放宿主专属入口。
13
+ * Panel toolbar. The module renders the mode switch; put host-specific
14
+ * entries here.
15
+ */
16
+ toolbar?: ReactNode;
17
+ /**
18
+ * 对话模式下输入区上方的附加内容,例如当前资源上下文。
19
+ * Extra content above the composer in conversation mode, for example the
20
+ * current resource context.
21
+ */
22
+ conversationContext?: ReactNode;
23
+ /**
24
+ * 命令模式下的命令帮助内容。省略时由模块按宿主声明的命令生成。
25
+ * Command help in command mode. When omitted, the module derives it from the
26
+ * commands the host declares.
27
+ */
28
+ commandHelp?: ReactNode;
29
+ }
30
+ /** 交互面板模块的可替换文案。Replaceable copy of the console module. */
31
+ export interface ConsoleLabels {
32
+ /**
33
+ * 占位说明。缺省为 `Placeholder. Functionality is not available yet.`。
34
+ * Placeholder description. Defaults to "Placeholder. Functionality is not
35
+ * available yet.".
36
+ */
37
+ description?: string;
38
+ /** 对话模式名称。Conversation mode label. */
39
+ conversation?: string;
40
+ /** 命令模式名称。Command mode label. */
41
+ command?: string;
42
+ /** 会话加载失败提示。Session load failure message. */
43
+ error?: string;
44
+ /** 重试按钮文案。Retry button label. */
45
+ retry?: string;
46
+ }
47
+ /**
48
+ * 交互面板模块的属性。
49
+ *
50
+ * 模块自己通过 Hook 取数,props 里没有会话与命令记录:宿主给配置、受控模式、
51
+ * 插槽与文案。打开资源通过回调交给宿主,与文件浏览模块的联动不依赖对方的私有
52
+ * 状态。
53
+ *
54
+ * Props of the console module.
55
+ *
56
+ * The module loads its own data through a Hook, so no prop carries sessions or
57
+ * command records: the host supplies configuration, the controlled mode, slots
58
+ * and copy. Opening a resource goes back to the host through a callback, and
59
+ * coordination with the file browser never reaches into the other module's
60
+ * private state.
61
+ */
62
+ export interface ConsoleProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect' | 'children'> {
63
+ /**
64
+ * 受控的当前模式。传入时由宿主决定模式,模块只上报切换意图。
65
+ * The controlled mode. When given, the host owns the mode and the module only
66
+ * reports switch intent.
67
+ */
68
+ mode?: ConsoleMode;
69
+ /**
70
+ * 非受控时的初始模式,缺省为 `conversation`。
71
+ * Initial mode when uncontrolled; defaults to `conversation`.
72
+ */
73
+ defaultMode?: ConsoleMode;
74
+ /**
75
+ * 模式变化后触发。
76
+ * Called after the mode changes.
77
+ */
78
+ onModeChange?: (mode: ConsoleMode) => void;
79
+ /**
80
+ * 资源成功打开后触发,参数为资源 URI;失败时不触发。
81
+ * Called after a resource opens successfully, with its URI; not called on
82
+ * failure.
83
+ */
84
+ onOpenUri?: (uri: string) => void;
85
+ /**
86
+ * 插槽,见 {@link ConsoleSlots}。
87
+ * Slots, see {@link ConsoleSlots}.
88
+ */
89
+ slots?: ConsoleSlots;
90
+ /**
91
+ * 可替换文案,缺省为英文。
92
+ * Replaceable copy; English by default.
93
+ */
94
+ labels?: ConsoleLabels;
95
+ /**
96
+ * 请求配置覆盖,逐字段覆盖最近的 Provider。
97
+ * Request configuration override, applied field by field over the nearest
98
+ * Provider.
99
+ */
100
+ requestOptions?: OvRequestOptions;
101
+ }
@@ -0,0 +1 @@
1
+ export { Console } from "../a~0.js";
@@ -0,0 +1,10 @@
1
+ import type { FileBrowserToolbarButtonProps } from './types.js';
2
+ /**
3
+ * 工具栏按钮。模块自己的搜索与刷新用它渲染,宿主在 `toolbarActions` 里也用它
4
+ * 组合入口,两边的外观因此保持一致。
5
+ *
6
+ * A toolbar button. The module renders its own search and refresh with it, and
7
+ * the host composes its entries with it inside `toolbarActions`, so both sides
8
+ * look the same.
9
+ */
10
+ export declare function FileBrowserToolbarButton({ icon, children, onClick, disabled, className, ...rest }: FileBrowserToolbarButtonProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { FileBrowserToolbarButton } from './file-browser-toolbar-button.js';
2
+ import type { FileBrowserProps } from './types.js';
3
+ export { FileBrowserToolbarButton };
4
+ export type { FileBrowserLabels, FileBrowserProps, FileBrowserToolbarButtonProps, } from './types.js';
5
+ /** 占位组件:渲染一个带模块名的空容器,真实实现随模块 Hook 与组合视图迁入。 */
6
+ export declare function FileBrowser({ className, style, labels }: FileBrowserProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export * from './file-browser.js';
@@ -0,0 +1,75 @@
1
+ .ov-file-browser__toolbar-button {
2
+ align-items: center;
3
+ gap: var(--ov-ui-space-2, 8px);
4
+ padding: var(--ov-ui-space-2, 8px) var(--ov-ui-space-3, 12px);
5
+ border-radius: var(--ov-ui-radius-sm, 4px);
6
+ color: var(--ov-ui-color-primary, #1664ff);
7
+ font: inherit;
8
+ cursor: pointer;
9
+ background: none;
10
+ border: 0;
11
+ display: inline-flex;
12
+ }
13
+
14
+ .ov-file-browser__toolbar-button:hover:not(:disabled) {
15
+ background: var(--ov-ui-color-fill-hover, #1664ff14);
16
+ }
17
+
18
+ .ov-file-browser__toolbar-button:focus-visible {
19
+ outline: 2px solid var(--ov-ui-color-focus, #1664ff);
20
+ outline-offset: 2px;
21
+ }
22
+
23
+ .ov-file-browser__toolbar-button:disabled {
24
+ color: var(--ov-ui-color-text-disabled, #00000040);
25
+ cursor: not-allowed;
26
+ }
27
+
28
+ .ov-file-browser__toolbar-button-icon {
29
+ flex: none;
30
+ display: inline-flex;
31
+ }
32
+
33
+ .ov-file-browser__toolbar-button-label {
34
+ white-space: nowrap;
35
+ }
36
+
37
+ .ov-file-browser__toolbar-actions > * + :before {
38
+ content: "";
39
+ width: 1px;
40
+ height: 16px;
41
+ margin-right: var(--ov-ui-space-2, 8px);
42
+ background: var(--ov-ui-color-border, #0000001f);
43
+ vertical-align: middle;
44
+ display: inline-block;
45
+ }
46
+
47
+ .ov-module-placeholder {
48
+ box-sizing: border-box;
49
+ border: 1px dashed var(--ov-ui-border, currentColor);
50
+ border-radius: var(--ov-ui-radius, 6px);
51
+ min-width: 0;
52
+ min-height: 160px;
53
+ color: inherit;
54
+ font: inherit;
55
+ text-align: center;
56
+ overflow-wrap: anywhere;
57
+ flex-direction: column;
58
+ justify-content: center;
59
+ align-items: center;
60
+ gap: 8px;
61
+ padding: 24px;
62
+ display: flex;
63
+ }
64
+
65
+ .ov-module-placeholder__title {
66
+ margin: 0;
67
+ }
68
+
69
+ .ov-module-placeholder__description {
70
+ color: var(--ov-ui-text-muted, color-mix(in srgb, currentColor 75%, transparent));
71
+ margin: 0;
72
+ }
73
+
74
+
75
+
File without changes
@@ -0,0 +1,101 @@
1
+ import type { OvRequestOptions } from '../../client/index.js';
2
+ import type { HTMLAttributes, ReactNode } from 'react';
3
+ /** 文件浏览模块的可替换文案。Replaceable copy of the file browser module. */
4
+ export interface FileBrowserLabels {
5
+ /**
6
+ * 占位说明。缺省为 `Placeholder. Functionality is not available yet.`。
7
+ * Placeholder description. Defaults to "Placeholder. Functionality is not
8
+ * available yet.".
9
+ */
10
+ description?: string;
11
+ /** 区域标题。Area title. */
12
+ title?: string;
13
+ /** 搜索框占位文案。Search placeholder. */
14
+ searchPlaceholder?: string;
15
+ /** 搜索无结果提示。Message shown when a search returns nothing. */
16
+ searchEmpty?: string;
17
+ /** 刷新按钮的无障碍名称。Accessible name of the refresh control. */
18
+ refresh?: string;
19
+ /** 未选中资源时的提示。Message shown when no resource is selected. */
20
+ empty?: string;
21
+ /** 加载失败提示。Load failure message. */
22
+ error?: string;
23
+ /** 重试按钮文案。Retry button label. */
24
+ retry?: string;
25
+ }
26
+ /**
27
+ * 工具栏按钮的属性。宿主在 `toolbarActions` 里用它组合自己的入口。
28
+ * Props of a toolbar button. The host composes its own entries with it inside
29
+ * `toolbarActions`.
30
+ */
31
+ export interface FileBrowserToolbarButtonProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'children' | 'onClick'> {
32
+ /** 按钮图标。Button icon. */
33
+ icon: ReactNode;
34
+ /** 按钮文案。Button label. */
35
+ children: ReactNode;
36
+ /** 是否禁用,缺省为 `false`。Whether the button is disabled; defaults to `false`. */
37
+ disabled?: boolean;
38
+ /** 点击后触发。Called on click. */
39
+ onClick?: () => void;
40
+ }
41
+ /**
42
+ * 文件浏览模块的属性。
43
+ *
44
+ * 模块自己通过 Hook 取数,props 里没有数据字段:宿主给配置、受控的当前资源、
45
+ * 工具栏入口、文案与请求配置。目录、搜索、预览页签与行级状态都在模块内维护,
46
+ * 宿主不需要重复组装。
47
+ *
48
+ * 模块只覆盖文件树与文件预览两块。命令面板属于 `console`。
49
+ *
50
+ * Props of the file browser module.
51
+ *
52
+ * The module loads its own data through a Hook, so no prop carries data: the
53
+ * host supplies configuration, the controlled current resource, the toolbar
54
+ * entries, copy and request configuration. The directory, search, preview tabs
55
+ * and per-row state all live inside the module, so the host does not assemble
56
+ * them again.
57
+ *
58
+ * The module covers only the file tree and the file preview. The command panel
59
+ * belongs to `console`.
60
+ */
61
+ export interface FileBrowserProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect' | 'children'> {
62
+ /**
63
+ * 受控的当前资源 URI。传入时由宿主决定当前资源,模块只上报切换意图。
64
+ * The controlled current resource URI. When given, the host owns the current
65
+ * resource and the module only reports switch intent.
66
+ */
67
+ uri?: string;
68
+ /**
69
+ * 非受控时的初始资源 URI。省略时不选中任何资源。
70
+ * Initial resource URI when uncontrolled. Nothing is selected when omitted.
71
+ */
72
+ defaultUri?: string;
73
+ /**
74
+ * 工具栏左侧的宿主入口,例如连接 Agent、导入资源。用
75
+ * {@link FileBrowserToolbarButton} 组合,保证与模块内置按钮外观一致。
76
+ * 搜索与刷新由模块渲染,入口之间的分隔线也由模块处理。
77
+ *
78
+ * Host entries on the left of the toolbar, for example connecting an agent or
79
+ * importing resources. Compose them with {@link FileBrowserToolbarButton} so
80
+ * they match the module's own buttons. Search and refresh are rendered by the
81
+ * module, and so are the dividers between entries.
82
+ */
83
+ toolbarActions?: ReactNode;
84
+ /**
85
+ * 当前资源变化后触发,参数为新的 URI;清除选中时传入 `null`。
86
+ * Called after the current resource changes, with the new URI; `null` when
87
+ * the selection is cleared.
88
+ */
89
+ onUriChange?: (uri: string | null) => void;
90
+ /**
91
+ * 可替换文案,缺省为英文。
92
+ * Replaceable copy; English by default.
93
+ */
94
+ labels?: FileBrowserLabels;
95
+ /**
96
+ * 请求配置覆盖,逐字段覆盖最近的 Provider。
97
+ * Request configuration override, applied field by field over the nearest
98
+ * Provider.
99
+ */
100
+ requestOptions?: OvRequestOptions;
101
+ }
@@ -0,0 +1 @@
1
+ export { FileBrowser, FileBrowserToolbarButton } from "../l~0.js";
@@ -0,0 +1,6 @@
1
+ import type { ScopeCascadeApi, ScopeCascadeController } from './types.js';
2
+ /** Instance-owned channels. Creating a controller does not start requests. */
3
+ export declare function createScopeController(): {
4
+ controller: ScopeCascadeController;
5
+ start(nextApi: ScopeCascadeApi): () => void;
6
+ };
@@ -0,0 +1,3 @@
1
+ export { ScopeCascade } from './scope-cascade.js';
2
+ export { useScopeCascade, useScopeCascadeLevel } from './use-scope-cascade.js';
3
+ export type { ScopeCascadeApi, ScopeCascadeController, ScopeCascadeIndex, ScopeCascadeLevel, ScopeCascadeProps, ScopeCascadeSnapshot, } from './types.js';
@@ -0,0 +1,3 @@
1
+ import type { ScopeCascadeProps } from './types.js';
2
+ /** Only leaf selectors subscribe. This wrapper stays outside the update path. */
3
+ export declare function ScopeCascade({ controller, labels, levelNames, retryLabel, ...props }: ScopeCascadeProps): import("react").JSX.Element;
@@ -0,0 +1,250 @@
1
+ .ov-scope-switcher {
2
+ box-sizing: border-box;
3
+ border: 1px solid var(--ov-ui-border, #edeef2);
4
+ background: var(--ov-ui-scope-background, transparent);
5
+ max-width: 100%;
6
+ min-height: 32px;
7
+ color: var(--ov-ui-text, #0b0b0f);
8
+ font-family: var(--ov-ui-font-family, "PingFang SC",
9
+ -apple-system,
10
+ BlinkMacSystemFont,
11
+ "Segoe UI",
12
+ sans-serif);
13
+ letter-spacing: .036px;
14
+ border-radius: 16px;
15
+ flex-wrap: wrap;
16
+ align-items: center;
17
+ gap: 4px;
18
+ padding: 3px 4px;
19
+ font-size: 12px;
20
+ line-height: 20px;
21
+ display: inline-flex;
22
+ }
23
+
24
+ .ov-scope-switcher *, .ov-scope-switcher :before, .ov-scope-switcher :after {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ .ov-scope-switcher__level {
29
+ align-items: center;
30
+ gap: 4px;
31
+ min-width: 0;
32
+ max-width: 100%;
33
+ display: inline-flex;
34
+ position: relative;
35
+ }
36
+
37
+ .ov-scope-switcher__level + .ov-scope-switcher__level:before {
38
+ content: "/";
39
+ color: var(--ov-ui-text-muted, #c9cbd4);
40
+ margin-right: 4px;
41
+ font-size: 13px;
42
+ font-weight: 500;
43
+ line-height: 22px;
44
+ }
45
+
46
+ .ov-scope-switcher__trigger {
47
+ max-width: min(260px, 100%);
48
+ height: 24px;
49
+ color: inherit;
50
+ font: inherit;
51
+ vertical-align: middle;
52
+ background: none;
53
+ border: 0;
54
+ border-radius: 99px;
55
+ align-items: center;
56
+ gap: 6px;
57
+ padding: 2px 8px;
58
+ display: inline-flex;
59
+ }
60
+
61
+ button.ov-scope-switcher__trigger {
62
+ cursor: pointer;
63
+ }
64
+
65
+ .ov-scope-switcher__trigger:hover:not(:disabled), .ov-scope-switcher__trigger[aria-expanded="true"] {
66
+ background: var(--ov-ui-hover, #edeffc);
67
+ }
68
+
69
+ .ov-scope-switcher__label {
70
+ white-space: nowrap;
71
+ text-overflow: ellipsis;
72
+ overflow: hidden;
73
+ }
74
+
75
+ .ov-scope-switcher__icon {
76
+ flex: none;
77
+ justify-content: center;
78
+ align-items: center;
79
+ width: 14px;
80
+ height: 14px;
81
+ display: inline-flex;
82
+ }
83
+
84
+ .ov-scope-switcher__icon img {
85
+ display: block;
86
+ }
87
+
88
+ .ov-scope-switcher__tag {
89
+ color: var(--ov-ui-text-muted, #9295a5);
90
+ font-size: 12px;
91
+ }
92
+
93
+ .ov-scope-switcher__arrow {
94
+ flex: 0 0 14px;
95
+ width: 14px;
96
+ height: 14px;
97
+ margin-left: -2px;
98
+ transition: transform .16s;
99
+ display: inline-flex;
100
+ }
101
+
102
+ .ov-scope-switcher__trigger[aria-expanded="true"] .ov-scope-switcher__arrow {
103
+ transform: rotate(180deg);
104
+ }
105
+
106
+ @media (prefers-reduced-motion: reduce) {
107
+ .ov-scope-switcher__arrow {
108
+ transition: none;
109
+ }
110
+ }
111
+
112
+ .ov-scope-switcher__panel {
113
+ z-index: 30;
114
+ border: 1px solid var(--ov-ui-border, #edeef2);
115
+ background: var(--ov-ui-surface, #fff);
116
+ width: min(272px, 100vw - 32px);
117
+ max-height: min(540px, 70vh);
118
+ color: var(--ov-ui-text, #0b0b0f);
119
+ letter-spacing: .039px;
120
+ border-radius: 12px;
121
+ padding: 7px;
122
+ font-size: 13px;
123
+ line-height: 22px;
124
+ position: absolute;
125
+ top: calc(100% + 8px);
126
+ left: 0;
127
+ overflow: auto;
128
+ box-shadow: 0 15px 35px -2px #0000000d, 0 5px 15px #0000000d;
129
+ }
130
+
131
+ .ov-scope-switcher__level:last-child:not(:first-child) .ov-scope-switcher__panel {
132
+ left: auto;
133
+ right: 0;
134
+ }
135
+
136
+ .ov-scope-switcher__search {
137
+ border: 1px solid var(--ov-ui-border, #dfe1e8);
138
+ width: 100%;
139
+ color: inherit;
140
+ font: inherit;
141
+ background: none;
142
+ border-radius: 8px;
143
+ margin-bottom: 8px;
144
+ padding: 8px 10px;
145
+ }
146
+
147
+ .ov-scope-switcher__list {
148
+ overscroll-behavior: contain;
149
+ flex-direction: column;
150
+ gap: 4px;
151
+ max-height: 280px;
152
+ display: flex;
153
+ overflow: auto;
154
+ }
155
+
156
+ .ov-scope-switcher__option {
157
+ width: 100%;
158
+ min-height: 44px;
159
+ color: inherit;
160
+ font: inherit;
161
+ text-align: start;
162
+ cursor: pointer;
163
+ background: none;
164
+ border: 0;
165
+ border-radius: 6px;
166
+ flex-shrink: 0;
167
+ align-items: center;
168
+ gap: 8px;
169
+ padding: 2px 8px;
170
+ display: flex;
171
+ }
172
+
173
+ .ov-scope-switcher__option:hover:not([aria-disabled="true"]), .ov-scope-switcher__option[aria-selected="true"] {
174
+ background: var(--ov-ui-hover, #f5f7ff);
175
+ }
176
+
177
+ .ov-scope-switcher__option-text {
178
+ flex-direction: column;
179
+ flex: 1;
180
+ min-width: 0;
181
+ display: flex;
182
+ overflow: hidden;
183
+ }
184
+
185
+ .ov-scope-switcher__option-text small {
186
+ color: var(--ov-ui-text-muted, #737889);
187
+ }
188
+
189
+ .ov-scope-switcher__option mark {
190
+ background: var(--ov-ui-highlight, #fff0b3);
191
+ color: inherit;
192
+ }
193
+
194
+ .ov-scope-switcher__check {
195
+ flex: 0 0 16px;
196
+ width: 16px;
197
+ height: 16px;
198
+ display: inline-flex;
199
+ }
200
+
201
+ .ov-scope-switcher__state {
202
+ color: var(--ov-ui-text-muted, #737889);
203
+ padding: 20px 10px;
204
+ }
205
+
206
+ .ov-scope-switcher button:disabled, .ov-scope-switcher__option[aria-disabled="true"] {
207
+ opacity: .5;
208
+ cursor: not-allowed;
209
+ }
210
+
211
+ .ov-scope-switcher :focus-visible {
212
+ outline: 2px solid var(--ov-ui-primary, #6256d9);
213
+ outline-offset: 2px;
214
+ }
215
+
216
+ @media (width <= 600px) {
217
+ .ov-scope-switcher__trigger {
218
+ max-width: 210px;
219
+ }
220
+
221
+ .ov-scope-switcher__panel, .ov-scope-switcher__level:last-child:not(:first-child) .ov-scope-switcher__panel {
222
+ width: auto;
223
+ max-height: 60dvh;
224
+ position: fixed;
225
+ inset: auto 16px 16px;
226
+ }
227
+ }
228
+
229
+ .ov-scope-switcher__option[aria-selected="true"] {
230
+ font-weight: 500;
231
+ }
232
+
233
+ .ov-scope-switcher__option-text > span {
234
+ text-overflow: ellipsis;
235
+ white-space: nowrap;
236
+ overflow: hidden;
237
+ }
238
+
239
+ .ov-scope-switcher__option .ov-scope-switcher__icon {
240
+ border: .5px solid var(--ov-ui-border, #edeef2);
241
+ background: var(--ov-ui-surface, #fff);
242
+ border-radius: 50%;
243
+ width: 24px;
244
+ height: 24px;
245
+ }
246
+
247
+
248
+
249
+
250
+
File without changes
@@ -0,0 +1,39 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ import type { ScopeSwitcherLabels, ScopeSwitcherOption } from '../../components/scope-switcher/index.js';
3
+ export interface ScopeCascadeApi {
4
+ loadLibraries: (signal: AbortSignal) => Promise<readonly ScopeSwitcherOption[]>;
5
+ loadSpaces: (libraryId: string, signal: AbortSignal) => Promise<readonly ScopeSwitcherOption[]>;
6
+ loadUsers: (libraryId: string, spaceId: string, signal: AbortSignal) => Promise<readonly ScopeSwitcherOption[]>;
7
+ }
8
+ export type ScopeCascadeIndex = 0 | 1 | 2;
9
+ export interface ScopeCascadeSnapshot {
10
+ readonly options: readonly ScopeSwitcherOption[];
11
+ readonly selected?: ScopeSwitcherOption;
12
+ readonly enabled: boolean;
13
+ readonly loading: boolean;
14
+ readonly error: Error | null;
15
+ /** Changes only when ancestor identities change. */
16
+ readonly scopeKey: string;
17
+ /** Ancestor IDs in library / space order, available to downstream consumers. */
18
+ readonly parentIds: readonly string[];
19
+ }
20
+ export interface ScopeCascadeLevel {
21
+ getSnapshot: () => ScopeCascadeSnapshot;
22
+ subscribe: (listener: () => void) => () => void;
23
+ select: (option: ScopeSwitcherOption) => void;
24
+ refresh: () => void;
25
+ }
26
+ export interface ScopeCascadeController {
27
+ readonly levels: readonly [
28
+ ScopeCascadeLevel,
29
+ ScopeCascadeLevel,
30
+ ScopeCascadeLevel
31
+ ];
32
+ }
33
+ export interface ScopeCascadeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
34
+ controller: ScopeCascadeController;
35
+ /** Accessible names and placeholders, in library / space / user order. */
36
+ levelNames?: readonly [string, string, string];
37
+ retryLabel?: string;
38
+ labels?: ScopeSwitcherLabels;
39
+ }
@@ -0,0 +1,5 @@
1
+ import type { ScopeCascadeApi, ScopeCascadeLevel } from './types.js';
2
+ /** Creates a stable coordinator without subscribing the caller to its data. */
3
+ export declare function useScopeCascade({ loadLibraries, loadSpaces, loadUsers, }: ScopeCascadeApi): import("./types.js").ScopeCascadeController;
4
+ /** Call in a leaf, never aggregate all three subscriptions in the root. */
5
+ export declare function useScopeCascadeLevel(level: ScopeCascadeLevel): import("./types.js").ScopeCascadeSnapshot;
@@ -0,0 +1 @@
1
+ export { ScopeCascade, useScopeCascade, useScopeCascadeLevel } from "../lk~0.js";