@undefineds.co/xpod 0.4.17 → 0.4.18

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 (134) hide show
  1. package/config/xpod.base.json +3 -0
  2. package/dist/components/context.jsonld +14 -0
  3. package/dist/identity/ValidatingIdentityProviderHttpHandler.d.ts +32 -1
  4. package/dist/identity/ValidatingIdentityProviderHttpHandler.js +53 -1
  5. package/dist/identity/ValidatingIdentityProviderHttpHandler.jsonld +53 -0
  6. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +39 -10
  7. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +15 -4
  8. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +1 -1
  9. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +4 -1
  10. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.d.ts +5 -1
  11. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.js +2 -2
  12. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +5 -1
  13. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +4 -0
  14. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +3 -1
  15. package/node_modules/@undefineds.co/extension-sdk/dist/layout.d.ts +39 -0
  16. package/node_modules/@undefineds.co/extension-sdk/dist/layout.js +40 -0
  17. package/node_modules/@undefineds.co/extension-sdk/dist/react/app-layout.d.ts +13 -1
  18. package/node_modules/@undefineds.co/extension-sdk/dist/react/app-layout.js +11 -3
  19. package/node_modules/@undefineds.co/extension-sdk/dist/react/workspace-layout.d.ts +13 -4
  20. package/node_modules/@undefineds.co/extension-sdk/dist/react/workspace-layout.js +68 -11
  21. package/node_modules/@undefineds.co/shared-ui/dist/auth-surface.js +6 -6
  22. package/node_modules/@undefineds.co/shared-ui/dist/dialog.js +4 -4
  23. package/node_modules/@undefineds.co/shared-ui/dist/login.js +1 -1
  24. package/node_modules/@undefineds.co/shared-ui/dist/theme.css +128 -79
  25. package/node_modules/@undefineds.co/shared-ui/dist/toast.js +1 -1
  26. package/node_modules/@undefineds.co/shared-ui/dist/tooltip.js +1 -1
  27. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.cjs +87 -9
  28. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.d.ts +31 -3
  29. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.js +156 -14
  30. package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +86 -8
  31. package/package.json +2 -2
  32. package/static/app/assets/global.css +1 -0
  33. package/static/app/assets/{index-BCZVzMpj.js → index-BeAi28iA.js} +1 -1
  34. package/static/app/assets/inrupt-smoke.js +37 -36
  35. package/static/app/assets/main.js +3 -3
  36. package/static/app/index.html +2 -2
  37. package/static/app/inrupt-smoke.html +2 -1
  38. package/static/dashboard/assets/{AiConfigContext-BedwuLTE.js → AiConfigContext-DelSqO6L.js} +1 -1
  39. package/static/dashboard/assets/{AiConfigPage-hNTayf8G.js → AiConfigPage-dbg0aDvd.js} +1 -1
  40. package/static/dashboard/assets/DocumentProcessingPanel-0-3nlwQf.js +1 -0
  41. package/static/dashboard/assets/IndexLifecyclePanel-DOI3IxOD.js +1 -0
  42. package/static/dashboard/assets/{IndexSubjectPanel-Bv97-ouZ.js → IndexSubjectPanel-ClDgj0SY.js} +1 -1
  43. package/static/dashboard/assets/ModelAssignmentsPanel-BSWH19oT.js +1 -0
  44. package/static/dashboard/assets/ModelsPage-BrzxCkEl.js +11 -0
  45. package/static/{settings/assets/NetworkPage-D50kdbEu.js → dashboard/assets/NetworkPage-CxEKeaPz.js} +2 -2
  46. package/static/dashboard/assets/{PaneListHeader-ByqK_C6i.js → PaneListHeader-xEjcBu9g.js} +1 -1
  47. package/static/dashboard/assets/SearchIndexingPanel-BGlSvEl5.js +1 -0
  48. package/static/dashboard/assets/{StatusSubjectPanel-DPegcFpA.js → StatusSubjectPanel-6s3JXbOd.js} +1 -1
  49. package/static/dashboard/assets/StatusWorkspace-LYMDa4b0.js +1 -0
  50. package/static/dashboard/assets/{SystemSettingsPage-LyXmd6uP.js → SystemSettingsPage-CF_vH-qe.js} +1 -1
  51. package/static/dashboard/assets/SystemSettingsSubjectPanel-hsfHcJRV.js +33 -0
  52. package/static/dashboard/assets/{XpodAiConnectionsPodStore-DXNERI2-.js → XpodAiConnectionsPodStore-CHM-ZwI3.js} +1 -1
  53. package/static/dashboard/assets/{activity-BwRbcTha.js → activity-B7UdTZ1N.js} +1 -1
  54. package/static/dashboard/assets/{badge-BbRb9MsQ.js → badge-B6RuxNt1.js} +1 -1
  55. package/static/dashboard/assets/collection-runtime-CKBQm3Fl.js +1 -0
  56. package/static/dashboard/assets/dashboard-1zrtbMhM.css +1 -0
  57. package/static/dashboard/assets/{dashboard-D2ozrATv.js → dashboard-Dm_h60lA.js} +175 -135
  58. package/static/dashboard/assets/{download-SIHe8Y5t.js → download-BW035kpj.js} +1 -1
  59. package/static/dashboard/assets/{external-link-CiYw5Q4U.js → external-link-BMrNvf3c.js} +1 -1
  60. package/static/dashboard/assets/{index-DC1jRaVA.js → index-2jlFzexm.js} +1 -1
  61. package/static/dashboard/assets/index-CQDQ8WVr.js +4 -0
  62. package/static/dashboard/assets/{key-round-nKtxP6pb.js → key-round-C9vxhn7T.js} +1 -1
  63. package/static/dashboard/assets/{rotate-ccw-CQm5hHMN.js → rotate-ccw-8Vm0-INc.js} +1 -1
  64. package/static/dashboard/assets/{skeleton-TKyLbiPs.js → skeleton-CY_602wn.js} +1 -1
  65. package/static/dashboard/assets/{waypoints-BQS9t0ky.js → waypoints-DdEdgkxD.js} +1 -1
  66. package/static/dashboard/assets/workspace-layout-Wed840qR.js +1 -0
  67. package/static/dashboard/dashboard.html +2 -2
  68. package/static/settings/assets/{AiConfigContext-eLRkXFtH.js → AiConfigContext-CR3YhTA-.js} +1 -1
  69. package/static/settings/assets/AiConfigPage-DnOvX2Kx.js +1 -0
  70. package/static/settings/assets/DocumentProcessingPanel-CccpHBs3.js +1 -0
  71. package/static/settings/assets/IndexLifecyclePanel-BrECzBoM.js +1 -0
  72. package/static/settings/assets/{IndexSubjectPanel-DT0c11VQ.js → IndexSubjectPanel-CP84xXye.js} +1 -1
  73. package/static/settings/assets/ModelAssignmentsPanel-DbFi67Kk.js +1 -0
  74. package/static/settings/assets/ModelsPage-xjHA88qy.js +11 -0
  75. package/static/settings/assets/NetworkPage-Bj4wc0bh.js +2 -0
  76. package/static/settings/assets/{PaneListHeader-Da5jZFvG.js → PaneListHeader-Bz0UAhZr.js} +1 -1
  77. package/static/settings/assets/SearchIndexingPanel-ykhvw1Vl.js +1 -0
  78. package/static/settings/assets/{StatusSubjectPanel-Upqqlvon.js → StatusSubjectPanel-B2Y6ib3d.js} +1 -1
  79. package/static/settings/assets/StatusWorkspace-tx3Xg56J.js +1 -0
  80. package/static/settings/assets/SystemSettingsPage-BbFkv_Wn.js +1 -0
  81. package/static/settings/assets/SystemSettingsSubjectPanel-CcbpQmTc.js +33 -0
  82. package/static/settings/assets/{XpodAiConnectionsPodStore-nFgD_usD.js → XpodAiConnectionsPodStore-B11u0BOi.js} +2 -2
  83. package/static/settings/assets/{activity-CWy1W2W0.js → activity-BE8Hz8WF.js} +1 -1
  84. package/static/settings/assets/auth-callback-5waKJF0n.js +1 -0
  85. package/static/settings/assets/{badge-v30yFaaj.js → badge-xeRbYBMf.js} +1 -1
  86. package/static/settings/assets/collection-runtime-DIBObcT5.js +1 -0
  87. package/static/settings/assets/{download-C3Dzb60R.js → download-xI5yyfk7.js} +1 -1
  88. package/static/settings/assets/{external-link-DnkJGKNc.js → external-link-DmbiCMuc.js} +1 -1
  89. package/static/settings/assets/global-1zrtbMhM.css +1 -0
  90. package/static/settings/assets/{global-B_WO-aYY.js → global-BOl78fr8.js} +160 -120
  91. package/static/settings/assets/index-CFVXiUnV.js +4 -0
  92. package/static/settings/assets/index-DK6qlahe.js +1 -0
  93. package/static/settings/assets/{index-browser-DzhsXZHm.js → index-browser-Bk7jaLCd.js} +1 -1
  94. package/static/settings/assets/{key-round-DoDbsLae.js → key-round-B3VIp8oI.js} +1 -1
  95. package/static/settings/assets/{rotate-ccw-DNR_R3oT.js → rotate-ccw-DRzTN49Z.js} +1 -1
  96. package/static/settings/assets/{settings-BOznrBjy.js → settings-CCM17CMX.js} +1 -1
  97. package/static/settings/assets/{skeleton-BjHhu9qI.js → skeleton-Ckhw5HG7.js} +1 -1
  98. package/static/settings/assets/{waypoints-BXNph9Fd.js → waypoints-BFMI-qS6.js} +1 -1
  99. package/static/settings/assets/workspace-layout-Co0KpMKd.js +1 -0
  100. package/static/settings/auth-callback.html +3 -3
  101. package/static/settings/settings.html +3 -3
  102. package/static/app/assets/main.css +0 -1
  103. package/static/dashboard/assets/DocumentProcessingPanel-BSb_vOj7.js +0 -1
  104. package/static/dashboard/assets/IndexLifecyclePanel-DAHx-PBG.js +0 -1
  105. package/static/dashboard/assets/ModelAssignmentsPanel-CEJZs9At.js +0 -1
  106. package/static/dashboard/assets/ModelsPage-DkBVXmKT.js +0 -11
  107. package/static/dashboard/assets/NetworkPage-BDqKgRZQ.js +0 -2
  108. package/static/dashboard/assets/SearchIndexingPanel-M-WYaQP3.js +0 -1
  109. package/static/dashboard/assets/StatusWorkspace-CPIkjO9S.js +0 -1
  110. package/static/dashboard/assets/SystemSettingsSubjectPanel-CYHqNIn2.js +0 -33
  111. package/static/dashboard/assets/collection-runtime-BRfZD5oS.js +0 -1
  112. package/static/dashboard/assets/dashboard-rk080mvG.css +0 -1
  113. package/static/dashboard/assets/dialog-BbRbILBP.js +0 -1
  114. package/static/dashboard/assets/index-CExLoewO.js +0 -41
  115. package/static/dashboard/assets/index-CTgU8YW1.js +0 -4
  116. package/static/dashboard/assets/workspace-layout-Csbbr3vZ.js +0 -1
  117. package/static/settings/assets/AiConfigPage-BMO-ytdK.js +0 -1
  118. package/static/settings/assets/DocumentProcessingPanel-CH-HBRzQ.js +0 -1
  119. package/static/settings/assets/IndexLifecyclePanel-ChEMn9D8.js +0 -1
  120. package/static/settings/assets/ModelAssignmentsPanel-T59L_wOk.js +0 -1
  121. package/static/settings/assets/ModelsPage-m_SP4PdE.js +0 -11
  122. package/static/settings/assets/SearchIndexingPanel-DQQd3-Jw.js +0 -1
  123. package/static/settings/assets/StatusWorkspace-CPB49bGd.js +0 -1
  124. package/static/settings/assets/SystemSettingsPage-C0wrxcNw.js +0 -1
  125. package/static/settings/assets/SystemSettingsSubjectPanel-BOm7tUH1.js +0 -33
  126. package/static/settings/assets/auth-callback-HhgEuohz.js +0 -1
  127. package/static/settings/assets/collection-runtime-B7oomxic.js +0 -1
  128. package/static/settings/assets/dialog-BAe7moyL.js +0 -1
  129. package/static/settings/assets/global-rk080mvG.css +0 -1
  130. package/static/settings/assets/index-B51ot_m_.js +0 -1
  131. package/static/settings/assets/index-CHBQVfuZ.js +0 -41
  132. package/static/settings/assets/index-DbqRFton.js +0 -4
  133. package/static/settings/assets/workspace-layout-TZ7fteel.js +0 -1
  134. /package/static/app/assets/{_commonjsHelpers-B-UnjaXt.js → global-BGMlqT2c.js} +0 -0
@@ -14,3 +14,42 @@ export type ThreePaneAppletLayoutDescriptor = {
14
14
  };
15
15
  export type AppletLayoutDescriptor = SinglePaneAppletLayoutDescriptor | TwoPaneAppletLayoutDescriptor | ThreePaneAppletLayoutDescriptor;
16
16
  export declare function defineAppletLayout<T extends AppletLayoutDescriptor>(descriptor: T): T;
17
+ /**
18
+ * §8.3 的四种页型。与上面的 pane 描述符并存:既有 applet 仍可用 three-pane,新页面按页型声明,
19
+ * 由 {@link resolveAppletPanePlan} 结合实际宽度与"是否真有对象集合"决定是否出现对象列。
20
+ */
21
+ export type AppletPageType = 'overview' | 'collection' | 'configuration' | 'diagnostics';
22
+ /**
23
+ * §8.3 断点的唯一来源,单位是内容视口 CSS px。页面不得自选断点。
24
+ */
25
+ /**
26
+ * 宽窗口导航列宽度(§8.3)。`AppLayout` 的 Tailwind 类必须与它一致
27
+ * (`md:grid-cols-[184px_minmax(0,1fr)]`),`packages/extension-sdk/test/app-layout.test.tsx` 两边都断言。
28
+ */
29
+ export declare const XPOD_LAYOUT_RAIL_WIDTH = 184;
30
+ export declare const XPOD_LAYOUT_BREAKPOINTS: {
31
+ /** 低于此宽度使用任务栏 + 导航抽屉,不强制底部 Tab。 */
32
+ readonly narrow: 768;
33
+ /** 只有达到此宽度且确有对象集合,才增加对象列。 */
34
+ readonly wideObjects: 1100;
35
+ };
36
+ export interface AppletPanePlanInput {
37
+ readonly pageType: AppletPageType;
38
+ /** 当前内容视口宽度(CSS px),不含原生标题栏与安全区。 */
39
+ readonly contentWidth: number;
40
+ /** 该页是否真有需要持续比较/切换的对象集合;概览、固定配置、诊断恒为 false。 */
41
+ readonly hasObjectCollection?: boolean;
42
+ }
43
+ export interface AppletPanePlan {
44
+ readonly pageType: AppletPageType;
45
+ readonly navigation: 'rail' | 'task-bar';
46
+ readonly panes: 'single' | 'stack' | 'list-detail';
47
+ readonly showObjectColumn: boolean;
48
+ }
49
+ /**
50
+ * 把页型、宽度与集合事实映射成布局计划(spec §8.3):
51
+ * - <768px:任务栏 + 导航抽屉,单面板;
52
+ * - 768–1099px:184px 文字导航,集合页用堆叠;
53
+ * - ≥1100px:只有真正集合才额外出现对象列。
54
+ */
55
+ export declare function resolveAppletPanePlan(input: AppletPanePlanInput): AppletPanePlan;
@@ -34,3 +34,43 @@ function assertValidLayoutContext(context) {
34
34
  throw new Error('Applet layout descriptor context.initiallyCollapsed must be a boolean');
35
35
  }
36
36
  }
37
+ /**
38
+ * §8.3 断点的唯一来源,单位是内容视口 CSS px。页面不得自选断点。
39
+ */
40
+ /**
41
+ * 宽窗口导航列宽度(§8.3)。`AppLayout` 的 Tailwind 类必须与它一致
42
+ * (`md:grid-cols-[184px_minmax(0,1fr)]`),`packages/extension-sdk/test/app-layout.test.tsx` 两边都断言。
43
+ */
44
+ export const XPOD_LAYOUT_RAIL_WIDTH = 184;
45
+ export const XPOD_LAYOUT_BREAKPOINTS = {
46
+ /** 低于此宽度使用任务栏 + 导航抽屉,不强制底部 Tab。 */
47
+ narrow: 768,
48
+ /** 只有达到此宽度且确有对象集合,才增加对象列。 */
49
+ wideObjects: 1100,
50
+ };
51
+ /**
52
+ * 把页型、宽度与集合事实映射成布局计划(spec §8.3):
53
+ * - <768px:任务栏 + 导航抽屉,单面板;
54
+ * - 768–1099px:184px 文字导航,集合页用堆叠;
55
+ * - ≥1100px:只有真正集合才额外出现对象列。
56
+ */
57
+ export function resolveAppletPanePlan(input) {
58
+ const { pageType, contentWidth } = input;
59
+ const hasCollection = input.hasObjectCollection === true;
60
+ if (!Number.isFinite(contentWidth) || contentWidth <= 0) {
61
+ throw new Error('Applet pane plan needs a positive contentWidth');
62
+ }
63
+ if (contentWidth < XPOD_LAYOUT_BREAKPOINTS.narrow) {
64
+ return { pageType, navigation: 'task-bar', panes: 'single', showObjectColumn: false };
65
+ }
66
+ const showObjectColumn = contentWidth >= XPOD_LAYOUT_BREAKPOINTS.wideObjects && pageType === 'collection' && hasCollection;
67
+ if (showObjectColumn) {
68
+ return { pageType, navigation: 'rail', panes: 'list-detail', showObjectColumn: true };
69
+ }
70
+ return {
71
+ pageType,
72
+ navigation: 'rail',
73
+ panes: pageType === 'collection' && contentWidth < XPOD_LAYOUT_BREAKPOINTS.wideObjects ? 'stack' : 'single',
74
+ showObjectColumn: false,
75
+ };
76
+ }
@@ -3,5 +3,17 @@ export interface AppLayoutProps {
3
3
  navigation: ReactNode;
4
4
  children: ReactNode;
5
5
  className?: string;
6
+ /** 窄窗任务栏的标题(spec §8.3)。宽窗由页面自己的标题承担。 */
7
+ narrowTitle?: ReactNode;
8
+ /** 提供时,窄窗任务栏显示返回按钮。 */
9
+ onNarrowBack?: () => void;
10
+ narrowBackLabel?: string;
11
+ /** 导航按钮与抽屉的名字;抽屉复用同一棵导航树。 */
12
+ navigationLabel?: string;
6
13
  }
7
- export declare function AppLayout({ navigation, children, className, }: AppLayoutProps): import("react").JSX.Element;
14
+ /**
15
+ * 工作区布局(spec §8.3):
16
+ * - ≥768px(Tailwind `md`,与 `XPOD_LAYOUT_BREAKPOINTS.narrow` 对齐):184px 文字导航 + 内容;
17
+ * - <768px:48px 顶部任务栏(返回、标题、有名称的导航按钮)+ 单面板,导航树放进抽屉,不强制底部 Tab。
18
+ */
19
+ export declare function AppLayout({ navigation, children, className, narrowTitle, onNarrowBack, narrowBackLabel, navigationLabel, }: AppLayoutProps): import("react").JSX.Element;
@@ -1,5 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from '@undefineds.co/shared-ui';
3
- export function AppLayout({ navigation, children, className, }) {
4
- return (_jsxs("section", { className: cn('grid h-screen min-h-0 grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)_64px] bg-background sm:grid-cols-[60px_minmax(0,1fr)] sm:grid-rows-[minmax(0,1fr)]', className), "data-app-layout": "workspace", children: [_jsx("aside", { className: "row-start-2 min-h-0 overflow-x-auto overflow-y-hidden border-t border-border/50 bg-layout-sidebar sm:col-start-1 sm:row-start-1 sm:overflow-hidden sm:border-r sm:border-t-0", "data-app-layout-navigation": true, children: navigation }), _jsx("div", { className: "col-start-1 row-start-1 min-h-0 min-w-0 overflow-hidden bg-layout-content sm:col-start-2", "data-app-layout-content": true, children: children })] }));
2
+ import { useState } from 'react';
3
+ import { ArrowLeft, Menu } from 'lucide-react';
4
+ import { cn, Dialog, DialogContent, DialogTitle, DialogTrigger, } from '@undefineds.co/shared-ui';
5
+ /**
6
+ * 工作区布局(spec §8.3):
7
+ * - ≥768px(Tailwind `md`,与 `XPOD_LAYOUT_BREAKPOINTS.narrow` 对齐):184px 文字导航 + 内容;
8
+ * - <768px:48px 顶部任务栏(返回、标题、有名称的导航按钮)+ 单面板,导航树放进抽屉,不强制底部 Tab。
9
+ */
10
+ export function AppLayout({ navigation, children, className, narrowTitle, onNarrowBack, narrowBackLabel = '返回', navigationLabel = '导航', }) {
11
+ const [drawerOpen, setDrawerOpen] = useState(false);
12
+ return (_jsxs("section", { className: cn('grid h-screen min-h-0 grid-cols-[minmax(0,1fr)] grid-rows-[48px_minmax(0,1fr)] bg-background md:grid-cols-[184px_minmax(0,1fr)] md:grid-rows-[minmax(0,1fr)]', className), "data-app-layout": "workspace", children: [_jsxs("header", { className: "col-start-1 row-start-1 flex h-12 min-w-0 items-center gap-1 border-b border-border/50 bg-layout-sidebar px-1 md:hidden", "data-app-layout-header": true, children: [onNarrowBack ? (_jsx("button", { type: "button", onClick: onNarrowBack, "aria-label": narrowBackLabel, className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-foreground hover:bg-accent focus:outline-none focus-visible:bg-accent", children: _jsx(ArrowLeft, { className: "h-5 w-5", "aria-hidden": "true" }) })) : null, _jsx("div", { className: "min-w-0 flex-1 truncate px-1 text-sm font-medium", "data-app-layout-title": true, children: narrowTitle }), _jsxs(Dialog, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "flex h-9 shrink-0 items-center gap-2 rounded-lg px-3 text-sm text-foreground hover:bg-accent focus:outline-none focus-visible:bg-accent", children: [_jsx(Menu, { className: "h-5 w-5", "aria-hidden": "true" }), navigationLabel] }) }), _jsxs(DialogContent, { variant: "sheet-left", "aria-describedby": undefined, children: [_jsx(DialogTitle, { className: "sr-only", children: navigationLabel }), navigation] })] })] }), _jsx("aside", { className: "row-start-2 hidden min-h-0 overflow-hidden border-border/50 bg-layout-sidebar md:row-start-1 md:block md:border-r", "data-app-layout-navigation": true, children: navigation }), _jsx("div", { className: "col-start-1 row-start-2 min-h-0 min-w-0 overflow-hidden bg-layout-content md:col-start-2 md:row-start-1", "data-app-layout-content": true, children: children })] }));
5
13
  }
@@ -1,7 +1,16 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { type AppletPageType } from '../layout';
2
3
  import { type WorkspaceLayoutMode, type WorkspaceLayoutPane } from './layout-context';
3
4
  export type TwoPaneLayoutMode = 'auto' | WorkspaceLayoutMode;
4
- export interface TwoPaneLayoutProps {
5
+ export interface WorkspacePageTypeProps {
6
+ /**
7
+ * §8.3 页型。概览、固定配置、诊断不会渲染对象列;只有 `collection` 且确有集合才在宽断点出现。
8
+ * 默认保持旧行为(集合页 + 有集合),新页面应显式声明。
9
+ */
10
+ pageType?: AppletPageType;
11
+ hasObjectCollection?: boolean;
12
+ }
13
+ export interface TwoPaneLayoutProps extends WorkspacePageTypeProps {
5
14
  listHeader: ReactNode;
6
15
  list: ReactNode;
7
16
  mainHeader: ReactNode;
@@ -19,7 +28,7 @@ export interface ThreePaneLayoutContextConfig {
19
28
  collapsible?: boolean;
20
29
  initiallyCollapsed?: boolean;
21
30
  }
22
- export interface ThreePaneLayoutProps {
31
+ export interface ThreePaneLayoutProps extends WorkspacePageTypeProps {
23
32
  header?: ReactNode;
24
33
  list: ReactNode;
25
34
  main: ReactNode;
@@ -33,6 +42,6 @@ export interface WorkspaceLayoutHistoryAdapter {
33
42
  push(pane: WorkspaceLayoutPane): void;
34
43
  subscribe(listener: (pane: WorkspaceLayoutPane) => void): () => void;
35
44
  }
36
- export declare function TwoPaneLayout({ listHeader, list, mainHeader, main, mode, history, className, }: TwoPaneLayoutProps): import("react").JSX.Element;
45
+ export declare function TwoPaneLayout({ listHeader, list, mainHeader, main, mode, history, className, pageType, hasObjectCollection, }: TwoPaneLayoutProps): import("react").JSX.Element;
37
46
  export declare function SinglePaneLayout({ header, main, className, }: SinglePaneLayoutProps): import("react").JSX.Element;
38
- export declare function ThreePaneLayout({ header, list, main, context, mode, history, contextConfig, className, }: ThreePaneLayoutProps): import("react").JSX.Element;
47
+ export declare function ThreePaneLayout({ header, list, main, context, mode, history, contextConfig, className, pageType, hasObjectCollection, }: ThreePaneLayoutProps): import("react").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
3
3
  import { cn } from '@undefineds.co/shared-ui';
4
+ import { resolveAppletPanePlan, XPOD_LAYOUT_BREAKPOINTS, XPOD_LAYOUT_RAIL_WIDTH, } from '../layout.js';
4
5
  import { WorkspaceLayoutContext, } from './layout-context.js';
5
6
  const stackMediaQuery = '(max-width: 767px)';
6
7
  const twoPaneGridStyle = {
@@ -33,6 +34,54 @@ function useResolvedMode(mode) {
33
34
  const autoMode = useSyncExternalStore(subscribeToStackModeChange, getAutoModeSnapshot, getServerAutoModeSnapshot);
34
35
  return mode === 'auto' ? autoMode : mode;
35
36
  }
37
+ /**
38
+ * 对象列的可用宽度媒体查询:§8.3 的 1100px 是**内容**宽度,宽窗口还有一个 184px 导航列,
39
+ * 所以视口阈值要加上它。
40
+ */
41
+ const objectColumnMediaQuery = `(min-width: ${XPOD_LAYOUT_BREAKPOINTS.wideObjects + XPOD_LAYOUT_RAIL_WIDTH}px)`;
42
+ /**
43
+ * 是否够放对象列。
44
+ *
45
+ * 没有 `matchMedia` 的环境(服务端渲染、旧测试环境)按宽窗口处理——与 `getServerAutoModeSnapshot`
46
+ * 的既有默认一致:产品主要跑在桌面壳里,未知环境不应把已有的两栏布局变成堆叠。
47
+ */
48
+ function useRoomForObjectColumn() {
49
+ return useSyncExternalStore((onStoreChange) => {
50
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
51
+ return () => undefined;
52
+ }
53
+ const media = window.matchMedia(objectColumnMediaQuery);
54
+ media.addEventListener('change', onStoreChange);
55
+ return () => media.removeEventListener('change', onStoreChange);
56
+ }, () => (typeof window === 'undefined' || typeof window.matchMedia !== 'function'
57
+ ? true
58
+ : window.matchMedia(objectColumnMediaQuery).matches), () => true);
59
+ }
60
+ /**
61
+ * 把 §8.3 的页型、宽度与集合事实落到布局上:复用 `resolveAppletPanePlan` 作为唯一判断处,
62
+ * 概览/固定配置/诊断在任何宽度都不出现对象列,集合页在 1100px 以下改为堆叠。
63
+ */
64
+ function useObjectColumn(input) {
65
+ const narrow = useSyncExternalStore(subscribeToStackModeChange, getAutoModeSnapshot, getServerAutoModeSnapshot) === 'stack';
66
+ const roomForObjects = useRoomForObjectColumn();
67
+ // 只需要区分三个区间,因此给 plan 一个能代表当前区间的宽度
68
+ const contentWidth = roomForObjects
69
+ ? XPOD_LAYOUT_BREAKPOINTS.wideObjects
70
+ : narrow
71
+ ? XPOD_LAYOUT_BREAKPOINTS.narrow - 1
72
+ : XPOD_LAYOUT_BREAKPOINTS.narrow;
73
+ const plan = resolveAppletPanePlan({
74
+ pageType: input.pageType,
75
+ contentWidth,
76
+ hasObjectCollection: input.hasObjectCollection,
77
+ });
78
+ // §8.3:只有真正的集合列出现时才是并排;其余(含窄窗的集合页)都走 list→detail 的堆叠
79
+ const autoMode = plan.panes === 'list-detail' ? 'split' : 'stack';
80
+ return {
81
+ resolvedMode: input.mode === 'auto' ? autoMode : input.mode,
82
+ showObjectColumn: plan.showObjectColumn,
83
+ };
84
+ }
36
85
  function useStackNavigation({ resolvedMode, history, resolvePane, }) {
37
86
  const [activePane, setActivePane] = useState('list');
38
87
  const focusPaneRef = useRef(null);
@@ -86,8 +135,11 @@ function useStackNavigation({ resolvedMode, history, resolvePane, }) {
86
135
  openContext,
87
136
  };
88
137
  }
89
- export function TwoPaneLayout({ listHeader, list, mainHeader, main, mode = 'auto', history, className, }) {
90
- const resolvedMode = useResolvedMode(mode);
138
+ export function TwoPaneLayout({ listHeader, list, mainHeader, main, mode = 'auto', history, className, pageType = 'collection', hasObjectCollection = true, }) {
139
+ const { resolvedMode } = useObjectColumn({ mode, pageType, hasObjectCollection });
140
+ // §8.3:非集合页不渲染对象列;集合页只有宽断点才显示
141
+ // §8.3:只有集合页才有对象列表;宽度决定它是并排的对象列还是堆叠的第一屏
142
+ const showList = pageType === 'collection' && hasObjectCollection;
91
143
  const { activePane, paneRefs, openList, openMain, openContext, } = useStackNavigation({
92
144
  resolvedMode,
93
145
  history,
@@ -101,15 +153,19 @@ export function TwoPaneLayout({ listHeader, list, mainHeader, main, mode = 'auto
101
153
  openContext,
102
154
  }), [activePane, openContext, openList, openMain, resolvedMode]);
103
155
  const isStack = resolvedMode === 'stack';
104
- const listHidden = isStack && activePane !== 'list';
105
- const mainHidden = isStack && activePane !== 'main';
106
- return (_jsx(WorkspaceLayoutContext.Provider, { value: navigation, children: _jsx("section", { className: cn('flex min-h-0 flex-1 flex-col bg-background', className), "data-workspace-layout": "two-pane", "data-workspace-mode": resolvedMode, children: _jsxs("div", { className: cn('grid min-h-0 flex-1', isStack ? 'grid-cols-1' : null), style: isStack ? undefined : twoPaneGridStyle, "data-workspace-layout-mode": mode, "data-workspace-active-pane": activePane, children: [_jsxs("aside", { ref: paneRefs.list, className: cn('min-h-0 flex-col overflow-hidden bg-layout-list-item @container', listHidden ? 'hidden' : 'flex', isStack ? 'border-r-0' : 'border-r border-border'), "data-testid": "workspace-list-pane", "data-workspace-pane": "list", hidden: listHidden, tabIndex: isStack ? -1 : undefined, children: [_jsx("header", { className: "h-12 shrink-0 border-b border-border bg-layout-list-header", "data-workspace-list-header": "true", children: listHeader }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: list })] }), _jsxs("main", { ref: paneRefs.main, className: cn('min-h-0 flex-col overflow-hidden bg-layout-content @container', mainHidden ? 'hidden' : 'flex'), "data-testid": "workspace-main-pane", "data-workspace-pane": "main", hidden: mainHidden, tabIndex: isStack ? -1 : undefined, children: [_jsx("header", { className: "h-12 shrink-0 border-b border-border bg-layout-content", "data-workspace-main-header": "true", children: mainHeader }), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [isStack ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openList, children: "\u8FD4\u56DE\u5217\u8868" })) : null, main] })] })] }) }) }));
156
+ const stacked = showList && isStack;
157
+ const listHidden = !showList || (stacked && activePane !== 'list');
158
+ const mainHidden = stacked && activePane !== 'main';
159
+ return (_jsx(WorkspaceLayoutContext.Provider, { value: navigation, children: _jsx("section", { className: cn('flex min-h-0 flex-1 flex-col bg-background', className), "data-workspace-layout": "two-pane", "data-workspace-mode": resolvedMode, children: _jsxs("div", { className: cn('grid min-h-0 flex-1', stacked || !showList ? 'grid-cols-1' : null), style: stacked || !showList ? undefined : twoPaneGridStyle, "data-workspace-layout-mode": mode, "data-workspace-object-column": showList && resolvedMode === 'split' ? 'shown' : 'hidden', "data-workspace-active-pane": activePane, children: [_jsxs("aside", { ref: paneRefs.list, className: cn('min-h-0 flex-col overflow-hidden bg-layout-list-item @container', listHidden ? 'hidden' : 'flex', stacked ? 'border-r-0' : 'border-r border-border'), "data-testid": "workspace-list-pane", "data-workspace-pane": "list", hidden: listHidden, tabIndex: stacked ? -1 : undefined, children: [_jsx("header", { className: "h-12 shrink-0 border-b border-border bg-layout-list-header", "data-workspace-list-header": "true", children: listHeader }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: list })] }), _jsxs("main", { ref: paneRefs.main, className: cn('min-h-0 flex-col overflow-hidden bg-layout-content @container', mainHidden ? 'hidden' : 'flex'), "data-testid": "workspace-main-pane", "data-workspace-pane": "main", hidden: mainHidden, tabIndex: stacked ? -1 : undefined, children: [_jsx("header", { className: "h-12 shrink-0 border-b border-border bg-layout-content", "data-workspace-main-header": "true", children: mainHeader }), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [stacked ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openList, children: "\u8FD4\u56DE\u5217\u8868" })) : null, main] })] })] }) }) }));
107
160
  }
108
161
  export function SinglePaneLayout({ header, main, className, }) {
109
162
  return (_jsxs("section", { className: cn('flex min-h-0 flex-1 flex-col bg-background', className), "data-workspace-layout": "single-pane", children: [header ? (_jsx("header", { className: "h-16 shrink-0 border-b border-border bg-layout-content", children: header })) : null, _jsx("main", { className: "min-h-0 flex-1 overflow-y-auto bg-layout-content @container", "data-testid": "workspace-content-pane", "data-workspace-pane": "content", children: main })] }));
110
163
  }
111
- export function ThreePaneLayout({ header, list, main, context, mode = 'auto', history, contextConfig, className, }) {
112
- const resolvedMode = useResolvedMode(mode);
164
+ export function ThreePaneLayout({ header, list, main, context, mode = 'auto', history, contextConfig, className, pageType = 'collection', hasObjectCollection = true, }) {
165
+ const { resolvedMode } = useObjectColumn({ mode, pageType, hasObjectCollection });
166
+ // §8.3:非集合页不渲染对象列
167
+ // §8.3:只有集合页才有对象列表;宽度决定它是并排的对象列还是堆叠的第一屏
168
+ const showList = pageType === 'collection' && hasObjectCollection;
113
169
  const [contextCollapsed, setContextCollapsed] = useState(contextConfig?.initiallyCollapsed ?? false);
114
170
  const { activePane, paneRefs, openList, openMain, openContext, } = useStackNavigation({ resolvedMode, history });
115
171
  const toggleContextCollapsed = useCallback(() => {
@@ -123,10 +179,11 @@ export function ThreePaneLayout({ header, list, main, context, mode = 'auto', hi
123
179
  openContext,
124
180
  }), [activePane, openContext, openList, openMain, resolvedMode]);
125
181
  const isStack = resolvedMode === 'stack';
126
- const listHidden = isStack && activePane !== 'list';
127
- const mainHidden = isStack && activePane !== 'main';
128
- const contextHidden = isStack
182
+ const stacked = showList && isStack;
183
+ const listHidden = !showList || (stacked && activePane !== 'list');
184
+ const mainHidden = stacked && activePane !== 'main';
185
+ const contextHidden = stacked
129
186
  ? activePane !== 'context'
130
187
  : Boolean(contextConfig?.collapsible && contextCollapsed);
131
- return (_jsx(WorkspaceLayoutContext.Provider, { value: navigation, children: _jsxs("section", { className: cn('flex min-h-0 flex-1 flex-col bg-background', className), "data-workspace-layout": "three-pane", "data-workspace-mode": resolvedMode, children: [header ? (_jsx("header", { className: "h-16 shrink-0 border-b border-border bg-layout-content", children: header })) : null, contextConfig?.collapsible && !isStack ? (_jsx("div", { className: "shrink-0 border-b border-border bg-layout-content px-3 py-2", children: _jsx("button", { type: "button", "aria-expanded": !contextCollapsed, className: "inline-flex items-center rounded-md px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground", onClick: toggleContextCollapsed, children: contextCollapsed ? '展开上下文面板' : '折叠上下文面板' }) })) : null, _jsxs("div", { className: cn('grid min-h-0 flex-1', isStack ? 'grid-cols-1' : null), style: isStack ? undefined : threePaneGridStyle, "data-workspace-layout-mode": mode, "data-workspace-active-pane": activePane, children: [_jsx("aside", { ref: paneRefs.list, className: cn('min-h-0 overflow-y-auto bg-layout-list-item @container', listHidden ? 'hidden' : null, isStack ? 'border-r-0' : 'border-r border-border'), "data-testid": "workspace-list-pane", "data-workspace-pane": "list", hidden: listHidden, tabIndex: isStack ? -1 : undefined, children: list }), _jsxs("main", { ref: paneRefs.main, className: cn('min-h-0 overflow-y-auto bg-layout-content @container', mainHidden ? 'hidden' : null), "data-testid": "workspace-main-pane", "data-workspace-pane": "main", hidden: mainHidden, tabIndex: isStack ? -1 : undefined, children: [isStack ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openList, children: "\u8FD4\u56DE\u5217\u8868" })) : null, main] }), _jsxs("aside", { ref: paneRefs.context, className: cn('min-h-0 overflow-y-auto bg-layout-content @container', contextHidden ? 'hidden' : null, isStack ? 'border-l-0' : 'border-l border-border'), "data-testid": "workspace-context-pane", "data-workspace-pane": "context", hidden: contextHidden, tabIndex: isStack ? -1 : undefined, children: [isStack ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openMain, children: "\u8FD4\u56DE\u4E3B\u533A\u57DF" })) : null, context] })] })] }) }));
188
+ return (_jsx(WorkspaceLayoutContext.Provider, { value: navigation, children: _jsxs("section", { className: cn('flex min-h-0 flex-1 flex-col bg-background', className), "data-workspace-layout": "three-pane", "data-workspace-mode": resolvedMode, children: [header ? (_jsx("header", { className: "h-16 shrink-0 border-b border-border bg-layout-content", children: header })) : null, contextConfig?.collapsible && !stacked ? (_jsx("div", { className: "shrink-0 border-b border-border bg-layout-content px-3 py-2", children: _jsx("button", { type: "button", "aria-expanded": !contextCollapsed, className: "inline-flex items-center rounded-md px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground", onClick: toggleContextCollapsed, children: contextCollapsed ? '展开上下文面板' : '折叠上下文面板' }) })) : null, _jsxs("div", { className: cn('grid min-h-0 flex-1', stacked ? 'grid-cols-1' : null), style: stacked || !showList ? undefined : threePaneGridStyle, "data-workspace-layout-mode": mode, "data-workspace-active-pane": activePane, children: [_jsx("aside", { ref: paneRefs.list, className: cn('min-h-0 overflow-y-auto bg-layout-list-item @container', listHidden ? 'hidden' : null, stacked ? 'border-r-0' : 'border-r border-border'), "data-testid": "workspace-list-pane", "data-workspace-pane": "list", hidden: listHidden, tabIndex: stacked ? -1 : undefined, children: list }), _jsxs("main", { ref: paneRefs.main, className: cn('min-h-0 overflow-y-auto bg-layout-content @container', mainHidden ? 'hidden' : null), "data-testid": "workspace-main-pane", "data-workspace-pane": "main", hidden: mainHidden, tabIndex: stacked ? -1 : undefined, children: [stacked ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openList, children: "\u8FD4\u56DE\u5217\u8868" })) : null, main] }), _jsxs("aside", { ref: paneRefs.context, className: cn('min-h-0 overflow-y-auto bg-layout-content @container', contextHidden ? 'hidden' : null, stacked ? 'border-l-0' : 'border-l border-border'), "data-testid": "workspace-context-pane", "data-workspace-pane": "context", hidden: contextHidden, tabIndex: stacked ? -1 : undefined, children: [stacked ? (_jsx("button", { type: "button", className: "inline-flex items-center px-4 py-3 text-sm text-muted-foreground hover:text-foreground", onClick: openMain, children: "\u8FD4\u56DE\u4E3B\u533A\u57DF" })) : null, context] })] })] }) }));
132
189
  }
@@ -86,16 +86,16 @@ export function AuthSurface({ mode, title, children, presentation = 'standard',
86
86
  const bodyClassName = isCompact
87
87
  ? 'flex min-h-0 flex-1 flex-col overflow-y-auto'
88
88
  : 'max-h-[min(80vh,48rem)] overflow-y-auto';
89
- const surfaceContent = (_jsxs(_Fragment, { children: [isCompact ? (_jsxs(_Fragment, { children: [_jsx("h1", { id: titleId, className: "sr-only", children: title }), mode === 'modal' && onClose && closeLabel ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": closeLabel, className: "absolute right-2 top-2 z-10", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })) : null] })) : (_jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-border/60 p-4", children: [_jsx("h1", { id: titleId, className: "text-lg font-semibold text-foreground", children: title }), mode === 'modal' && onClose && closeLabel ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": closeLabel, children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })) : null] })), lead ? (_jsx("div", { "data-testid": "auth-surface-lead", className: cn(isCompact
89
+ const surfaceContent = (_jsxs(_Fragment, { children: [isCompact ? (_jsxs(_Fragment, { children: [_jsx("h1", { id: titleId, className: "sr-only", children: title }), mode === 'modal' && onClose && closeLabel ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": closeLabel, className: "absolute right-2 top-2 z-[var(--layer-sticky)]", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })) : null] })) : (_jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-border/60 p-4", children: [_jsx("h1", { id: titleId, className: "text-lg font-semibold text-foreground", children: title }), mode === 'modal' && onClose && closeLabel ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": closeLabel, children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })) : null] })), lead ? (_jsx("div", { "data-testid": "auth-surface-lead", className: cn(isCompact
90
90
  ? 'shrink-0 px-5 pt-7 has-[[data-presentation=compact]]:pt-5'
91
91
  : 'border-b border-border/60 p-4'), children: lead })) : null, _jsx(AuthSurfaceBody, { className: cn(bodyClassName, contentClassName), children: children })] }));
92
92
  return (_jsx("div", { "data-testid": `auth-surface-${mode}`, "data-auth-surface-mode": mode, "data-auth-surface-presentation": isCompact ? 'compact' : undefined, "data-auth-surface-host": isWindowHost ? 'window' : undefined, className: cn(isWindowHost
93
- ? 'fixed inset-0 z-50 flex items-stretch justify-stretch overflow-hidden bg-card p-0'
93
+ ? 'fixed inset-0 z-[var(--layer-modal)] flex items-stretch justify-stretch overflow-hidden bg-card p-0'
94
94
  : mode === 'modal'
95
- ? cn('fixed inset-0 z-50 flex items-center justify-center p-4', isCompact ? 'bg-black/50' : 'bg-background')
95
+ ? cn('fixed inset-0 z-[var(--layer-modal)] flex items-center justify-center p-4',
96
+ // §5.1:compact 用主题画布铺满,不铺遮罩
97
+ 'bg-background')
96
98
  : mode === 'page'
97
99
  ? 'flex min-h-[100dvh] w-full items-center justify-center bg-background p-4'
98
- : 'flex min-h-0 w-full items-center justify-center p-4', className), children: isWindowHost ? (_jsx("div", { ref: surfaceRef, ...dialogProps, "data-auth-surface-frame": "window", tabIndex: -1, className: "relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-card text-card-foreground focus:outline-none", children: surfaceContent })) : (_jsx(Card, { ref: surfaceRef, ...dialogProps, tabIndex: -1, style: { outline: 'none' }, className: cn('relative flex min-h-0 w-full flex-col border-border bg-card text-card-foreground focus:outline-none', isCompact
99
- ? 'h-[400px] w-[280px] max-h-[calc(100dvh-2rem)] max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border-border/50 shadow-lg shadow-black/5'
100
- : mode === 'embedded' ? 'max-w-none shadow-none' : 'max-w-lg'), children: surfaceContent })) }));
100
+ : 'flex min-h-0 w-full items-center justify-center p-4', className), children: isWindowHost ? (_jsx("div", { ref: surfaceRef, ...dialogProps, "data-auth-surface-frame": "window", tabIndex: -1, className: "relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-card text-card-foreground focus:outline-none", children: surfaceContent })) : isCompact ? (_jsx("div", { ref: surfaceRef, ...dialogProps, "data-auth-surface-frame": "compact", tabIndex: -1, style: { outline: 'none' }, className: "flex h-[400px] w-[280px] max-h-[calc(100dvh-2rem)] max-w-[calc(100vw-2rem)] min-w-0 flex-col overflow-y-auto overscroll-contain focus:outline-none", children: surfaceContent })) : (_jsx(Card, { ref: surfaceRef, ...dialogProps, tabIndex: -1, style: { outline: 'none' }, className: cn('relative flex min-h-0 w-full flex-col border-border bg-card text-card-foreground focus:outline-none', mode === 'embedded' ? 'max-w-none shadow-none' : 'max-w-lg'), children: surfaceContent })) }));
101
101
  }
@@ -8,13 +8,13 @@ const Dialog = DialogPrimitive.Root;
8
8
  const DialogTrigger = DialogPrimitive.Trigger;
9
9
  const DialogPortal = DialogPrimitive.Portal;
10
10
  const DialogClose = DialogPrimitive.Close;
11
- const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-black/40", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
11
+ const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-[var(--layer-backdrop)] xpod-overlay-scrim", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
12
12
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
13
  const DialogContent = React.forwardRef(({ className, children, variant = "center", ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn(variant === "sheet-left"
14
- ? "fixed inset-y-0 left-0 z-50 flex h-dvh w-[min(88vw,320px)] flex-col border-r bg-background p-0 shadow-[16px_0_30px_rgba(15,23,42,0.08)] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-left-full rounded-none"
14
+ ? "fixed inset-y-0 left-0 z-[var(--layer-modal)] flex h-dvh w-[min(88vw,320px)] flex-col border-r bg-background p-0 shadow-[16px_0_30px_rgba(15,23,42,0.08)] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-left-full rounded-none"
15
15
  : variant === "sheet-right"
16
- ? "fixed inset-y-0 right-0 z-50 flex h-dvh w-full max-w-4xl flex-col border-l bg-background p-0 shadow-[-16px_0_30px_rgba(15,23,42,0.08)] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-right-full rounded-none"
17
- : "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg", className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: cn("absolute right-4 top-4 rounded-xl opacity-70 transition-all hover:opacity-100 hover:bg-muted/50 p-1 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", interactiveFocusClass), children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
16
+ ? "fixed inset-y-0 right-0 z-[var(--layer-modal)] flex h-dvh w-full max-w-4xl flex-col border-l bg-background p-0 shadow-[-16px_0_30px_rgba(15,23,42,0.08)] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-right-full rounded-none"
17
+ : "fixed left-[50%] top-[50%] z-[var(--layer-modal)] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg", className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: cn("absolute right-4 top-4 rounded-xl opacity-70 transition-all hover:opacity-100 hover:bg-muted/50 p-1 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", interactiveFocusClass), children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
18
18
  DialogContent.displayName = DialogPrimitive.Content.displayName;
19
19
  const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
20
20
  DialogHeader.displayName = "DialogHeader";
@@ -32,7 +32,7 @@ export function LoginCardShell({ children, ariaLabel, overlayClassName, cardClas
32
32
  const baseCardClassName = cardSize === 'auto'
33
33
  ? 'w-compact-modal warm-card overflow-hidden rounded-xl flex flex-col'
34
34
  : 'w-compact-modal h-compact-modal warm-card overflow-hidden rounded-xl flex flex-col';
35
- return (_jsx("div", { "data-testid": surfaceTestId, "data-auth-surface-host": surfaceHost, className: cn('fixed inset-0 z-[999] flex items-center justify-center bg-black/50', overlayClassName), children: _jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": ariaLabel, "data-login-card-size": cardSize, className: cn(baseCardClassName, cardClassName), children: children }) }));
35
+ return (_jsx("div", { "data-testid": surfaceTestId, "data-auth-surface-host": surfaceHost, className: cn('fixed inset-0 z-[var(--layer-backdrop)] flex items-center justify-center xpod-overlay-scrim', overlayClassName), children: _jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": ariaLabel, "data-login-card-size": cardSize, className: cn(baseCardClassName, cardClassName), children: children }) }));
36
36
  }
37
37
  export function LoginErrorBanner({ error, onDismiss, dismissLabel, }) {
38
38
  if (!error)
@@ -1,27 +1,27 @@
1
1
  @layer base {
2
2
  :root {
3
3
  color-scheme: light;
4
- --background: 0 0% 98%;
5
- --foreground: 0 0% 12%;
6
- --card: 0 0% 100%;
7
- --card-foreground: 0 0% 12%;
8
- --popover: 0 0% 100%;
9
- --popover-foreground: 0 0% 12%;
10
- --primary: 252 46% 57%;
11
- --primary-foreground: 0 0% 100%;
12
- --secondary: 0 0% 96%;
13
- --secondary-foreground: 0 0% 12%;
14
- --muted: 0 0% 96%;
15
- --muted-foreground: 0 0% 43%;
16
- --accent: 0 0% 96%;
17
- --accent-foreground: 0 0% 12%;
4
+ --background: 42 38.5% 94.9%;
5
+ --foreground: 30 13.2% 14.9%;
6
+ --card: 45 33.3% 97.6%;
7
+ --card-foreground: 30 13.2% 14.9%;
8
+ --popover: 45 33.3% 97.6%;
9
+ --popover-foreground: 30 13.2% 14.9%;
10
+ --primary: 260.6 36.1% 38%;
11
+ --primary-foreground: 42 38.5% 94.9%;
12
+ --secondary: 0 10.2% 90.4%;
13
+ --secondary-foreground: 30 13.2% 14.9%;
14
+ --muted: 18 15.6% 87.5%;
15
+ --muted-foreground: 33.3 9.8% 36.1%;
16
+ --accent: 324 7.5% 86.9%;
17
+ --accent-foreground: 30 13.2% 14.9%;
18
18
  --destructive: 352 43% 49%;
19
19
  --destructive-foreground: 0 0% 100%;
20
- --border: 0 0% 88%;
21
- --input: 0 0% 88%;
22
- --ring: 252 46% 57%;
23
- --lineage: 252 46% 57%;
24
- --lineage-foreground: 0 0% 100%;
20
+ --border: 36 13.5% 85.5%;
21
+ --input: 36 4% 51%;
22
+ --ring: 260.6 36.1% 38%;
23
+ --lineage: 260.6 36.1% 38%;
24
+ --lineage-foreground: 42 38.5% 94.9%;
25
25
  --boundary: 202 34% 37%;
26
26
  --boundary-foreground: 0 0% 100%;
27
27
  --success: 160 48% 30%;
@@ -36,92 +36,136 @@
36
36
  --radius-2xl: 16px;
37
37
  --radius-3xl: 16px;
38
38
  --radius: 0.5rem;
39
- --layout-sidebar: 0 0% 96%;
40
- --layout-list-header: 0 0% 100%;
41
- --layout-list-hover: 0 0% 96%;
42
- --layout-list-item: 0 0% 100%;
43
- --layout-list-selected: 252 24% 94%;
44
- --layout-content: 0 0% 100%;
45
- --purple-medium: 252 46% 57%;
46
- --purple-deep: 252 40% 46%;
39
+ --layout-sidebar: 41.3 38.1% 91.8%;
40
+ --layout-list-header: 42 38.5% 94.9%;
41
+ --layout-list-hover: 0 10.2% 90.4%;
42
+ --layout-list-item: 45 33.3% 97.6%;
43
+ --layout-list-selected: 324 7.5% 86.9%;
44
+ --layout-content: 42 38.5% 94.9%;
45
+ --purple-medium: 260.6 36.1% 38%;
46
+ --purple-deep: 261.2 35.7% 28%;
47
47
  --purple-light: 258 48% 84%;
48
+ --control-strong: 33.3 9.8% 36.1%;
49
+ --action-hover: 261 35.7% 32.9%;
50
+ --action-pressed: 261.2 35.7% 28%;
51
+ --tint-canvas: 0 10.2% 90.4%;
52
+ --tint-sunken: 18 15.6% 87.5%;
53
+ --tint-badge: 324 7.5% 86.9%;
54
+ /* 公共叠层:§8.2 层级表的唯一来源,页面不得自带数值 */
55
+ --layer-base: 0;
56
+ --layer-sticky: 10;
57
+ --layer-popover: 20;
58
+ --layer-backdrop: 30;
59
+ --layer-modal: 40;
60
+ --layer-toast: 50;
61
+ --scrim: 30 13.8% 11.4%;
48
62
  }
49
63
 
50
64
  .dark {
51
65
  color-scheme: dark;
52
- --background: 0 0% 8%;
53
- --foreground: 0 0% 96%;
54
- --card: 0 0% 10%;
55
- --card-foreground: 0 0% 96%;
56
- --popover: 0 0% 10%;
57
- --popover-foreground: 0 0% 96%;
58
- --primary: 252 48% 65%;
59
- --primary-foreground: 0 0% 100%;
60
- --secondary: 0 0% 15%;
61
- --secondary-foreground: 0 0% 96%;
62
- --muted: 0 0% 15%;
63
- --muted-foreground: 0 0% 68%;
64
- --accent: 0 0% 15%;
65
- --accent-foreground: 0 0% 96%;
66
+ --background: 30 13.8% 11.4%;
67
+ --foreground: 42 38.5% 94.9%;
68
+ --card: 30 13.2% 14.9%;
69
+ --card-foreground: 42 38.5% 94.9%;
70
+ --popover: 30 13.2% 14.9%;
71
+ --popover-foreground: 42 38.5% 94.9%;
72
+ --primary: 270 16.7% 71.8%;
73
+ --primary-foreground: 30 13.8% 11.4%;
74
+ --secondary: 276.9 11.7% 21.8%;
75
+ --secondary-foreground: 42 38.5% 94.9%;
76
+ --muted: 276.9 11.7% 21.8%;
77
+ --muted-foreground: 34.3 15.8% 73.9%;
78
+ --accent: 276.9 11.7% 21.8%;
79
+ --accent-foreground: 42 38.5% 94.9%;
66
80
  --destructive: 352 43% 49%;
67
81
  --destructive-foreground: 0 0% 100%;
68
- --border: 0 0% 21%;
69
- --input: 0 0% 21%;
70
- --ring: 252 48% 65%;
71
- --lineage: 252 48% 65%;
72
- --lineage-foreground: 0 0% 100%;
82
+ --border: 30 12.3% 25.5%;
83
+ --input: 32.7 9.2% 53.3%;
84
+ --ring: 270 16.7% 71.8%;
85
+ --lineage: 270 16.7% 71.8%;
86
+ --lineage-foreground: 30 13.8% 11.4%;
73
87
  --boundary: 202 34% 48%;
74
88
  --boundary-foreground: 0 0% 100%;
75
89
  --success: 160 48% 42%;
76
90
  --success-foreground: 0 0% 100%;
77
91
  --warning: 44 72% 48%;
78
92
  --warning-foreground: 0 0% 100%;
79
- --layout-sidebar: 0 0% 15%;
80
- --layout-list-header: 0 0% 8%;
81
- --layout-list-hover: 0 0% 15%;
82
- --layout-list-item: 0 0% 8%;
83
- --layout-list-selected: 252 14% 18%;
84
- --layout-content: 0 0% 8%;
93
+ --layout-sidebar: 30 13.6% 8.6%;
94
+ --layout-list-header: 30 13.8% 11.4%;
95
+ --layout-list-hover: 276.9 11.7% 21.8%;
96
+ --layout-list-item: 30 13.2% 14.9%;
97
+ --layout-list-selected: 276.9 11.7% 21.8%;
98
+ --layout-content: 30 13.8% 11.4%;
99
+ --control-strong: 33.9 13.6% 66.9%;
100
+ --action-hover: 271.6 16.8% 77.8%;
101
+ --action-pressed: 264.4 18.8% 66.7%;
102
+ --tint-canvas: 276.9 11.7% 21.8%;
103
+ --tint-sunken: 276.9 11.7% 21.8%;
104
+ --tint-badge: 276.9 11.7% 21.8%;
105
+ --purple-medium: 270 16.7% 71.8%;
106
+ --purple-deep: 264.4 18.8% 66.7%;
107
+ /* 公共叠层:§8.2 层级表的唯一来源,页面不得自带数值 */
108
+ --layer-base: 0;
109
+ --layer-sticky: 10;
110
+ --layer-popover: 20;
111
+ --layer-backdrop: 30;
112
+ --layer-modal: 40;
113
+ --layer-toast: 50;
114
+ --scrim: 30 13.8% 11.4%;
85
115
  }
86
116
 
87
117
  .light {
88
118
  color-scheme: light;
89
- --background: 0 0% 98%;
90
- --foreground: 0 0% 12%;
91
- --card: 0 0% 100%;
92
- --card-foreground: 0 0% 12%;
93
- --popover: 0 0% 100%;
94
- --popover-foreground: 0 0% 12%;
95
- --primary: 252 46% 57%;
96
- --primary-foreground: 0 0% 100%;
97
- --secondary: 0 0% 96%;
98
- --secondary-foreground: 0 0% 12%;
99
- --muted: 0 0% 96%;
100
- --muted-foreground: 0 0% 43%;
101
- --accent: 0 0% 96%;
102
- --accent-foreground: 0 0% 12%;
119
+ --background: 42 38.5% 94.9%;
120
+ --foreground: 30 13.2% 14.9%;
121
+ --card: 45 33.3% 97.6%;
122
+ --card-foreground: 30 13.2% 14.9%;
123
+ --popover: 45 33.3% 97.6%;
124
+ --popover-foreground: 30 13.2% 14.9%;
125
+ --primary: 260.6 36.1% 38%;
126
+ --primary-foreground: 42 38.5% 94.9%;
127
+ --secondary: 0 10.2% 90.4%;
128
+ --secondary-foreground: 30 13.2% 14.9%;
129
+ --muted: 18 15.6% 87.5%;
130
+ --muted-foreground: 33.3 9.8% 36.1%;
131
+ --accent: 324 7.5% 86.9%;
132
+ --accent-foreground: 30 13.2% 14.9%;
103
133
  --destructive: 352 43% 49%;
104
134
  --destructive-foreground: 0 0% 100%;
105
- --border: 0 0% 88%;
106
- --input: 0 0% 88%;
107
- --ring: 252 46% 57%;
108
- --lineage: 252 46% 57%;
109
- --lineage-foreground: 0 0% 100%;
135
+ --border: 36 13.5% 85.5%;
136
+ --input: 36 4% 51%;
137
+ --ring: 260.6 36.1% 38%;
138
+ --lineage: 260.6 36.1% 38%;
139
+ --lineage-foreground: 42 38.5% 94.9%;
110
140
  --boundary: 202 34% 37%;
111
141
  --boundary-foreground: 0 0% 100%;
112
142
  --success: 160 48% 30%;
113
143
  --success-foreground: 0 0% 100%;
114
144
  --warning: 44 72% 36%;
115
145
  --warning-foreground: 0 0% 100%;
116
- --layout-sidebar: 0 0% 96%;
117
- --layout-list-header: 0 0% 100%;
118
- --layout-list-hover: 0 0% 96%;
119
- --layout-list-item: 0 0% 100%;
120
- --layout-list-selected: 252 24% 94%;
121
- --layout-content: 0 0% 100%;
122
- --purple-medium: 252 46% 57%;
123
- --purple-deep: 252 40% 46%;
146
+ --layout-sidebar: 41.3 38.1% 91.8%;
147
+ --layout-list-header: 42 38.5% 94.9%;
148
+ --layout-list-hover: 0 10.2% 90.4%;
149
+ --layout-list-item: 45 33.3% 97.6%;
150
+ --layout-list-selected: 324 7.5% 86.9%;
151
+ --layout-content: 42 38.5% 94.9%;
152
+ --purple-medium: 260.6 36.1% 38%;
153
+ --purple-deep: 261.2 35.7% 28%;
124
154
  --purple-light: 258 48% 84%;
155
+ --control-strong: 33.3 9.8% 36.1%;
156
+ --action-hover: 261 35.7% 32.9%;
157
+ --action-pressed: 261.2 35.7% 28%;
158
+ --tint-canvas: 0 10.2% 90.4%;
159
+ --tint-sunken: 18 15.6% 87.5%;
160
+ --tint-badge: 324 7.5% 86.9%;
161
+ /* 公共叠层:§8.2 层级表的唯一来源,页面不得自带数值 */
162
+ --layer-base: 0;
163
+ --layer-sticky: 10;
164
+ --layer-popover: 20;
165
+ --layer-backdrop: 30;
166
+ --layer-modal: 40;
167
+ --layer-toast: 50;
168
+ --scrim: 30 13.8% 11.4%;
125
169
  }
126
170
 
127
171
  html,
@@ -216,6 +260,11 @@
216
260
  }
217
261
 
218
262
  @layer components {
263
+ /* 弹层遮罩的唯一实现:颜色来自 --scrim,透明度不散落在页面 */
264
+ .xpod-overlay-scrim {
265
+ background-color: hsl(var(--scrim) / 0.45);
266
+ }
267
+
219
268
  .warm-card {
220
269
  border: 1px solid hsl(var(--border) / 0.5);
221
270
  border-radius: var(--radius-xl);