dsh-plugin-core 0.33.3 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adv/session/SessionViewModal.d.ts +20 -2
- package/dist/adv/session/SessionViewModal.d.ts.map +1 -1
- package/dist/adv/session/SessionViewModal.js +186 -73
- package/dist/adv/session/SessionViewModal.js.map +1 -1
- package/dist/adv/session/faces.d.ts +30 -7
- package/dist/adv/session/faces.d.ts.map +1 -1
- package/dist/adv/session/index.d.ts +1 -0
- package/dist/adv/session/index.d.ts.map +1 -1
- package/dist/adv/session/index.js +1 -0
- package/dist/adv/session/index.js.map +1 -1
- package/dist/adv/session/mirror/BashRow.d.ts.map +1 -1
- package/dist/adv/session/mirror/BashRow.js +11 -2
- package/dist/adv/session/mirror/BashRow.js.map +1 -1
- package/dist/adv/session/mirror/ChatView.d.ts +8 -0
- package/dist/adv/session/mirror/ChatView.d.ts.map +1 -1
- package/dist/adv/session/mirror/ChatView.js +8 -0
- package/dist/adv/session/mirror/ChatView.js.map +1 -1
- package/dist/adv/session/mirror/ContextBody.d.ts +24 -0
- package/dist/adv/session/mirror/ContextBody.d.ts.map +1 -0
- package/dist/adv/session/mirror/ContextBody.js +249 -0
- package/dist/adv/session/mirror/ContextBody.js.map +1 -0
- package/dist/adv/session/mirror/ContextInjectionRow.d.ts +21 -1
- package/dist/adv/session/mirror/ContextInjectionRow.d.ts.map +1 -1
- package/dist/adv/session/mirror/ContextInjectionRow.js +57 -29
- package/dist/adv/session/mirror/ContextInjectionRow.js.map +1 -1
- package/dist/adv/session/mirror/GenericCommandCard.d.ts +20 -0
- package/dist/adv/session/mirror/GenericCommandCard.d.ts.map +1 -1
- package/dist/adv/session/mirror/GenericCommandCard.js +89 -27
- package/dist/adv/session/mirror/GenericCommandCard.js.map +1 -1
- package/dist/adv/session/mirror/MessageIconActions.js +1 -1
- package/dist/adv/session/mirror/MessageIconActions.js.map +1 -1
- package/dist/adv/session/mirror/MessageImage.d.ts +23 -0
- package/dist/adv/session/mirror/MessageImage.d.ts.map +1 -0
- package/dist/adv/session/mirror/MessageImage.js +107 -0
- package/dist/adv/session/mirror/MessageImage.js.map +1 -0
- package/dist/adv/session/mirror/MessageItem.d.ts +11 -0
- package/dist/adv/session/mirror/MessageItem.d.ts.map +1 -1
- package/dist/adv/session/mirror/MessageItem.js +12 -2
- package/dist/adv/session/mirror/MessageItem.js.map +1 -1
- package/dist/adv/session/official-classes.d.ts +2 -0
- package/dist/adv/session/official-classes.d.ts.map +1 -1
- package/dist/adv/session/official-classes.js +3 -0
- package/dist/adv/session/official-classes.js.map +1 -1
- package/dist/adv/session/open-session-view.d.ts +6 -1
- package/dist/adv/session/open-session-view.d.ts.map +1 -1
- package/dist/adv/session/open-session-view.js +8 -0
- package/dist/adv/session/open-session-view.js.map +1 -1
- package/dist/adv/session/session-css.generated.d.ts +1 -1
- package/dist/adv/session/session-css.generated.d.ts.map +1 -1
- package/dist/adv/session/session-css.generated.js +1 -1
- package/dist/adv/session/session-css.generated.js.map +1 -1
- package/dist/design/tokens-css.generated.d.ts +1 -1
- package/dist/design/tokens-css.generated.d.ts.map +1 -1
- package/dist/design/tokens-css.generated.js +1 -1
- package/dist/design/tokens-css.generated.js.map +1 -1
- package/dist/ui/feedback/Modal.d.ts +7 -0
- package/dist/ui/feedback/Modal.d.ts.map +1 -1
- package/dist/ui/feedback/Modal.js +2 -0
- package/dist/ui/feedback/Modal.js.map +1 -1
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/infra/overlay-css.generated.d.ts +1 -1
- package/dist/ui/infra/overlay-css.generated.d.ts.map +1 -1
- package/dist/ui/infra/overlay-css.generated.js +1 -1
- package/dist/ui/infra/overlay-css.generated.js.map +1 -1
- package/dist/ui/infra/overlay.d.ts +7 -0
- package/dist/ui/infra/overlay.d.ts.map +1 -1
- package/dist/ui/infra/overlay.js +6 -1
- package/dist/ui/infra/overlay.js.map +1 -1
- package/dist/ui/infra/panel-css.generated.d.ts +1 -1
- package/dist/ui/infra/panel-css.generated.d.ts.map +1 -1
- package/dist/ui/infra/panel-css.generated.js +1 -1
- package/dist/ui/infra/panel-css.generated.js.map +1 -1
- package/dist/ui/inputs/DateTimePicker.d.ts +5 -1
- package/dist/ui/inputs/DateTimePicker.d.ts.map +1 -1
- package/dist/ui/inputs/DateTimePicker.js +55 -7
- package/dist/ui/inputs/DateTimePicker.js.map +1 -1
- package/dist/ui/inputs/Picker.d.ts +2 -0
- package/dist/ui/inputs/Picker.d.ts.map +1 -1
- package/dist/ui/inputs/Picker.js +25 -8
- package/dist/ui/inputs/Picker.js.map +1 -1
- package/dist/ui/inputs/datetime-css.generated.d.ts +1 -1
- package/dist/ui/inputs/datetime-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/datetime-css.generated.js +1 -1
- package/dist/ui/inputs/datetime-css.generated.js.map +1 -1
- package/dist/ui/inputs/datetime-logic.d.ts +13 -0
- package/dist/ui/inputs/datetime-logic.d.ts.map +1 -1
- package/dist/ui/inputs/datetime-logic.js +68 -0
- package/dist/ui/inputs/datetime-logic.js.map +1 -1
- package/dist/ui/inputs/field-css.generated.d.ts +1 -1
- package/dist/ui/inputs/field-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/field-css.generated.js +1 -1
- package/dist/ui/inputs/field-css.generated.js.map +1 -1
- package/dist/ui/inputs/input-group-css.generated.d.ts +1 -1
- package/dist/ui/inputs/input-group-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/input-group-css.generated.js +1 -1
- package/dist/ui/inputs/input-group-css.generated.js.map +1 -1
- package/dist/ui/inputs/picker-css.generated.d.ts +1 -1
- package/dist/ui/inputs/picker-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/picker-css.generated.js +1 -1
- package/dist/ui/inputs/picker-css.generated.js.map +1 -1
- package/dist/ui/inputs/picker-logic.d.ts +5 -1
- package/dist/ui/inputs/picker-logic.d.ts.map +1 -1
- package/dist/ui/inputs/picker-logic.js +16 -2
- package/dist/ui/inputs/picker-logic.js.map +1 -1
- package/dist/ui/layout/panel-dock-css.generated.d.ts +1 -1
- package/dist/ui/layout/panel-dock-css.generated.d.ts.map +1 -1
- package/dist/ui/layout/panel-dock-css.generated.js +1 -1
- package/dist/ui/layout/panel-dock-css.generated.js.map +1 -1
- package/dist/ui/layout/split-group-css.generated.d.ts +1 -1
- package/dist/ui/layout/split-group-css.generated.d.ts.map +1 -1
- package/dist/ui/layout/split-group-css.generated.js +1 -1
- package/dist/ui/layout/split-group-css.generated.js.map +1 -1
- package/dist/utils/i18n.d.ts +2 -2
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +57 -1
- package/dist/utils/i18n.js.map +1 -1
- package/docs/design/external/dsh-host.md +22 -0
- package/docs/design/style.md +38 -10
- package/docs/help/session.doc.ts +19 -2
- package/docs/help/session.md +19 -2
- package/docs/help/ui.doc.ts +16 -0
- package/docs/help/ui.md +16 -0
- package/package.json +1 -1
package/docs/help/session.md
CHANGED
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
|
|
31
31
|
> EN: The snapshot is read-only, so you cannot type inside the dialog. Continuing means `forkSession` + `openHostSession`; the staging input’s confirm goes through `onContinue` (fork + first turn + navigate).
|
|
32
32
|
|
|
33
|
+
**内容插槽**(都可选,都是 `ReactNode`,缺省不渲染 —— DOM 与不传时逐字一致):`title` 换标题行(sessionId 副标题行仍在);`header` 整块换左侧标题区(连 sessionId 行一起,归档徽标改挂动作区最前);`headerActions` 追加在「继续对话」**左边**(继续对话与 ✕ 恒居右);`bodyHeader` 落在会话滚动容器内最顶(自带固定下边距 `--dshpc-space-3`,不跟随相邻项的 margin);`bodyFooter` 落在最后一条回复下方、交付文件 / 变更卡之后、复制 / 分支 / 用量操作行之上(末尾没有回复时退回流的末尾)。两个内容槽都随会话一起滚,加载中 / 空 / 出错 / 正常四态都渲染。槽内容由调用方自己渲染,核心不取数、不做数据注入;弹窗与调用方在同一文档内,槽里直接用 `--dshpc-*` token 与核心样式即可生效。标题是一整块 `ReactNode`,放链接 / 标签 / 按钮都行(点击不会冒泡关窗);文字与徽标混排时用 `inline-flex` + `align-items:center` 包一层,否则徽标会贴基线显得下沉。
|
|
34
|
+
|
|
35
|
+
> EN: **Content slots** (all optional `ReactNode`s; absent means nothing is rendered — the DOM stays byte-identical): `title` replaces the title row (the sessionId subtitle stays); `header` replaces the whole left title area (including the sessionId row, moving the archived badge to the front of the header actions); `headerActions` is appended after "Continue" and before the close button; `bodyHeader` / `bodyFooter` sit at the very top / bottom inside the session scroller, scroll with the session, and render in all four states (loading / empty / error / normal). Slot content belongs to the caller — core fetches nothing and injects no data; the dialog shares the document, so `--dshpc-*` tokens and core styles just work inside a slot.
|
|
36
|
+
|
|
33
37
|
文件出口只有一个:工具卡路径、行内引用、交付卡都走 `onOpenFile`;不传则链接降级为纯文本(可以点但没有出口,不是坏了)。自己已有预览面的,在回调里调你自己的(核心一个 DOM 都不碰);没有的,一行接上核心自带的右侧分栏:`onOpenFile: (path, sid) => openFilePreview({ mount: '#page-row', path, sessionId: sid })`——落点给元素或选择器都行,弹窗不需要任何遮罩参数。
|
|
34
38
|
|
|
35
39
|
> EN: There is exactly one file exit: tool-card paths, inline mentions and delivered cards all go through `onOpenFile`; without it links degrade to plain text (clickable-looking but no exit — not a break). If you already own a preview surface, call yours inside the callback (core touches no DOM); otherwise wire core’s dock in one line: `onOpenFile: (path, sid) => openFilePreview({ mount: '#page-row', path, sessionId: sid })` — the mount takes an element or a selector, and the dialog needs no mask props.
|
|
@@ -56,7 +60,12 @@
|
|
|
56
60
|
| `sessionId` | `string` | 是 | — | 要预览的会话 | The session to preview |
|
|
57
61
|
| `view` | `SessionViewTarget` | 是 | — | openSessionView 的返回值 | What openSessionView returned |
|
|
58
62
|
| `onClose` | `() => void` | 是 | — | 关闭回调;由调用方在里面调 view.dispose() | Close callback; the caller calls view.dispose() inside it |
|
|
59
|
-
| `
|
|
63
|
+
| `title` | `ReactNode` | 否 | 「会话预览」 | 标题行;通常传宿主 sessions.list 的 displayTitle,也可放任意节点(后面跟标签 / 按钮)。会话 id 仍在下方副标题行(等宽小字),不受影响 | Title row; usually the displayTitle from the host sessions.list, or any node (tags / buttons after it). The session id stays on the monospace subtitle line below, unaffected |
|
|
64
|
+
| `heading` | `string` | 否 | — | 0.34 的旧名,等价 `title`(只收字符串);两者都给以 `title` 为准 | Legacy name from 0.34, equal to `title` (string only); `title` wins when both are given |
|
|
65
|
+
| `header` | `ReactNode` | 否 | — | 整块替换左侧标题区(标题行 + sessionId 行):传了则 `title` / `heading` 不生效,归档徽标改挂到右侧动作区最前;「继续对话」与关闭 ✕ 不受影响 | Replaces the whole left title area (title row + sessionId row): `title` / `heading` stop applying, and the archived badge moves to the front of the header actions; "Continue" and ✕ are unaffected |
|
|
66
|
+
| `headerActions` | `ReactNode` | 否 | — | 头部右侧追加动作:排在核心「继续对话」**左边**(「继续对话」与关闭 ✕ 恒居最右) | Extra header actions, placed to the **left** of core "Continue" (Continue and ✕ always stay rightmost) |
|
|
67
|
+
| `bodyHeader` | `ReactNode` | 否 | — | 会话内容最顶端(滚动容器内、所有节点与「加载更早」之前),自带固定下边距(`--dshpc-space-3`);随会话一起滚,加载中 / 空 / 出错 / 正常四态都渲染 | Very top of the session content (inside the scroller, before all nodes and "load earlier"), with a fixed bottom gap (`--dshpc-space-3`); scrolls with the session and renders in all four states (loading / empty / error / normal) |
|
|
68
|
+
| `bodyFooter` | `ReactNode` | 否 | — | 最后一条回复下方、交付文件 / 变更卡**之后**、复制 / 分支 / 用量操作行**之上**(末尾没有回复或没有尾操作行时落在流的末尾);随会话一起滚,四态都渲染 | Below the last reply, after the delivered-files / changed-files cards and **above** the copy / branch / usage action row (falls to the end of the flow when there is no trailing reply or action row); scrolls with the session and renders in all four states |
|
|
60
69
|
| `forkSession` | `(sessionId, atSeq?) => Promise<string>` | — | — | 开分支并返回子会话 id;已归档会话的「继续对话」始终渲染,缺它时确认框内如实说明能力未就位 | Fork and resolve the child session id; "Continue" stays rendered for archived sessions, and the dialog states plainly when this capability is missing |
|
|
61
70
|
| `openHostSession` | `(sessionId: string) => void` | — | — | 跳官方会话区(uiWorkspace.openSession) | Jump into the official session area (uiWorkspace.openSession) |
|
|
62
71
|
| `onContinue` | `(message: string) => Promise<void>` | — | — | 暂存输入框的确定:调用方完成 fork + 发首轮 + 跳转;缺省 = 不渲染输入框 | Staging input confirm: caller forks, sends the first turn, navigates; absent = no input rendered |
|
|
@@ -74,7 +83,15 @@ if (view !== null) {
|
|
|
74
83
|
h(SessionViewModal, {
|
|
75
84
|
sessionId,
|
|
76
85
|
view,
|
|
77
|
-
|
|
86
|
+
title: '镜像升级日报', // 标题行,缺省「会话预览」
|
|
87
|
+
// 插槽(都可选,缺省不渲染 —— DOM 与不传时一致):
|
|
88
|
+
// 标题行加节点,sessionId 行仍在;文字与徽标混排要用 inline-flex,否则徽标贴基线会下沉
|
|
89
|
+
// title: h('span', { style: { display: 'inline-flex', alignItems: 'center', gap: '6px' } },
|
|
90
|
+
// '日报', h(Chip, { size: 'xs' }, '运行中')),
|
|
91
|
+
// header: h(TaskHead, { taskId }), // 整块换左侧标题区(含 sessionId 行;归档徽标改挂动作区最前)
|
|
92
|
+
// headerActions: h(Button, null, '导出'), // 排在「继续对话」左边(继续对话与 ✕ 恒居右)
|
|
93
|
+
// bodyHeader: h(ContextBar), // 会话滚动容器内最顶,随会话滚
|
|
94
|
+
// bodyFooter: h(SessionSummary), // 交付文件之后、复制/分支/用量操作行之上;没回复则在最底
|
|
78
95
|
onClose: () => { view.dispose(); setOpen(false) }, // 关闭必须 dispose
|
|
79
96
|
// 续聊(可选):fork 开分支 → 跳官方会话区;缺 openHostSession 才不渲染该入口
|
|
80
97
|
forkSession: (id, atSeq) => sessions.fork({ sessionId: id, increaseTitle: true, atSeq }),
|
package/docs/help/ui.doc.ts
CHANGED
|
@@ -382,6 +382,10 @@ const doc: DocTopic = {
|
|
|
382
382
|
zh: 'Picker:单 / 多选、上限、搜索、分组、空态,自绘。键盘:Esc 关、↑↓ 移游标、Enter 选。纯逻辑可单用:`toGroups` / `filterGroups` / `toggleValue`。触发器支持 width / minWidth / maxWidth 约束宽度:定宽忽略另两者;内容超长则省略号 + hover 跑马灯。菜单里的选项超长同样省略 + hover 跑马灯,不会把菜单撑出横向滚动条。',
|
|
383
383
|
en: 'Picker: single/multiple selection, cap, search, grouping, empty state — self-drawn. Keyboard: Esc closes, ↑↓ move the cursor, Enter selects. Logic reusable standalone: `toGroups` / `filterGroups` / `toggleValue`.',
|
|
384
384
|
},
|
|
385
|
+
{
|
|
386
|
+
zh: '首屏分页:`limit` 按**选项条数**跨组切片,列表末尾「显示更多」(跟在最后一条后,像多一条选项)每点一次再追加 `limit` 条;搜索结果走同一套分页,换搜索词或重新展开都回到首屏。不传 `limit` 就是全量渲染。`PickerOption.keywords` 是可选的单个字符串(调用方自己拼),只和 `label` 拼起来参与大小写不敏感子串匹配,**绝不渲染**——适合任务 UUID 这种「用户能贴、但不该显示」的搜索词。纯逻辑另可单用 `sliceGroups`。',
|
|
387
|
+
en: 'First-page paging: `limit` slices by **option count** across groups; "Show more" rendered as the last row of the list appends another `limit` each click. Search results use the same paging and reset to the first page on a new query or reopen. No `limit` means render everything. `PickerOption.keywords` is an optional single string (joined by the caller) that participates in case-insensitive substring matching together with `label` but is **never rendered** — for search terms like a task UUID that users paste but should not see. `sliceGroups` is also exported as pure logic.',
|
|
388
|
+
},
|
|
385
389
|
{
|
|
386
390
|
zh: 'WorkspacePicker:数据自带的领域选择器。在插件 apply 里调一次 `connectHost(ctx)`,组件即订阅官方 `workspaces` 快照。未接线 = 空态,不编造数据。',
|
|
387
391
|
en: 'WorkspacePicker: a domain picker that brings its own data. Call `connectHost(ctx)` once in the plugin apply and it subscribes to the official `workspaces` snapshot. Unwired = empty state, never fabricated data.',
|
|
@@ -401,6 +405,8 @@ const doc: DocTopic = {
|
|
|
401
405
|
{ name: 'mode', type: "'single' | 'multiple'", def: "'single'", desc: { zh: '—', en: '—' } },
|
|
402
406
|
{ name: 'max', type: 'number', def: '—', desc: { zh: '多选上限;到上限其余项置灰 + 提示(不静默截断)', en: 'Multiple-select cap; at cap others grey out + a hint (no silent truncation)' } },
|
|
403
407
|
{ name: 'searchable', type: 'boolean', def: "'false'", desc: { zh: '显示搜索框;展开菜单时自动聚焦搜索框', en: 'Shows a search box; auto-focuses it when the menu opens' } },
|
|
408
|
+
{ name: 'limit', type: 'number', def: '—', desc: { zh: '首屏显示的选项条数(跨组计);列表末尾「显示更多」每次再追加同样条数,搜索结果同样分页。不传 = 全量', en: 'Options shown on the first page (counted across groups); "Show more" appends the same amount each click, search results included. Omitted = all' } },
|
|
409
|
+
{ name: 'options[].keywords', type: 'string', def: '—', desc: { zh: '只参与搜索、不渲染的额外关键词(单个字符串,调用方自行拼接)', en: 'Extra search-only keyword, never rendered (one string, joined by the caller)' } },
|
|
404
410
|
{ name: 'align', type: "'left' | 'center'", def: "'left'", desc: { zh: '触发器文字对齐', en: 'Trigger text alignment' } },
|
|
405
411
|
{ name: 'clearable', type: 'boolean', def: "'false'", desc: { zh: '单选时列表顶部多一项「不选」', en: 'In single mode, adds a "none" item at the top of the list' } },
|
|
406
412
|
{ name: 'placeholder', type: 'string', def: '内置「请选择」', desc: { zh: '—', en: '—' } },
|
|
@@ -525,6 +531,14 @@ const doc: DocTopic = {
|
|
|
525
531
|
zh: '弹层与 Picker 同机制(floating-ui 浮层、贴边翻转、点外关闭、无遮罩):两个端框 + 两块独立月历(左开始、右结束,各选各的,不标跨度)+ 每端自己的时/分/秒步进(▲▼、直接键入、↑↓ 键——不用逐格点 60 分钟;**时间不强制先选日期**,「确定」时没选日期就不传数据)。快捷键:day 精度叫「今天」(只置日期),带时分秒的叫「现在」(日期 + 时间一起置,之后可再改)。草稿态,「确定」才 onChange;Esc / 点外 = 丢弃。日历键盘:方向键移动、PageUp/Down 换月、Home/End 月内首尾、Enter 选中。结束早于开始时「确定」置灰并提示。',
|
|
526
532
|
en: 'The popup shares Picker’s mechanics (floating-ui layer, edge flip, outside-click close, no mask): two end boxes + two independent calendars (left = start, right = end, each picks its own, no span marking) + per-end hour/minute/second steppers (▲▼, direct typing, ↑↓ keys — no clicking through 60 minutes; **time never requires picking a date first** — on confirm, no date means no data emitted). Quick button: "Today" at day precision (date only), "Now" when hours/minutes/seconds are on (sets date + time together, editable afterwards). Draft state; onChange only on confirm; Esc / outside = discard. Calendar keys: arrows move, PageUp/Down change month, Home/End jump to month bounds, Enter selects. When end precedes start, confirm greys out with a hint.',
|
|
527
533
|
},
|
|
534
|
+
{
|
|
535
|
+
zh: '`presets` 只在 range 生效:弹层拆成左区(两个端框 + 两块日历,同宽、上下对齐)与右侧一整列快捷按钮(从端框那行竖排到日历底部,一条竖线隔开,按钮多了自己滚)。**列宽固定容下英文缩写最长那档**(中文两字居中),列高严格等于左侧日历区,档位多了只在列内滚动、不把弹层撑高;按钮文字居中。快捷按钮(`size="sm"`)与两个端框(`size="md"`)**都复用核心 `Button`**(选中态走 `color` 轴),不另写皮肤、圆角与全站一致;`label` 由调用方给,给 `{ zh, en }` 双语对象即随 `setLocale` 自动切换(核心不内置文案)。`expr` 用 Grafana / Elasticsearch 的 date math 子集——`now/d` 今天、`now-1d/d` 昨天、`now/w` 本周、`now-1w/w` 上周、`now/M` 本月、`now-1M/M` 上月、`now/Q` 本季度、`now-1Q/Q` 上季度、`now/y` 今年、`now-1y/y` 去年(`Q` 季、`M` 月、`m` 分),`expr: null` 即清空起止。点一下**立刻 onChange 且面板不关**,当场看得见两端与日历一起变。选中态只按当前起止与各项算出的起止逐一比对:撞上就亮,一个没撞上就整排不亮(不显示「自定义」);每次渲染按当天重算,不用记状态。',
|
|
536
|
+
en: '`presets` applies to range only: the popup splits into a left zone (two end boxes + two calendars, same width and aligned) and one full-height shortcut column on the right (runs from the end-box row down past the calendars, separated by a vertical rule, scrolls when crowded). **Column width is fixed to fit the longest short English label** (two Chinese characters sit centered), its height exactly matches the left calendar zone, and extra items scroll inside the column without growing the popup; button text is centered. Shortcut buttons (`size="sm"`) and both end boxes (`size="md"`) **reuse the core `Button`** (active via the `color` axis) — no local skin, corner radius matches the rest of the kit; labels come from the caller, and a `{ zh, en }` object switches with `setLocale` (no built-in copy). `expr` uses the Grafana / Elasticsearch date math subset — `now/d` today, `now-1d/d` yesterday, `now/w` this week, `now-1w/w` last week, `now/M` this month, `now-1M/M` last month, `now/Q` this quarter, `now-1Q/Q` last quarter, `now/y` this year, `now-1y/y` last year (`Q` quarter, `M` month, `m` minute); `expr: null` clears both ends. A click **emits immediately without closing the popup**, so both ends and the calendars visibly jump together. The active state is pure value comparison against each preset’s resolved range: matches light up, no match leaves the whole column unlit (no "Custom" item). Ranges are recomputed against the current day on every render; no state is kept.',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
zh: '`value` 永远只放具体日期:短词只是「怎么算出这两个日期」的配方,不进值(与 Ant Design `presets` / MUI X `shortcuts` 同口径),因此不存在「这是时间点还是相对表达式」的字符串歧义。`width` 覆盖**整个触发器**(数字按 px、字符串按 CSS 长度原样),直接盖掉按 precision 写死的默认宽,核心不换算;两个日期区仍平分,过窄自动省略。不传 `presets` 就没有右侧快捷列与竖线;不传 `width` 时触发器仍按 precision 走默认宽。两个端框现已统一到核心 `Button` 皮肤,range 弹层外观与全站一致。',
|
|
540
|
+
en: '`value` always carries concrete dates only: the short expression is merely the recipe for computing those two dates and never enters the value (same shape as Ant Design `presets` / MUI X `shortcuts`), so there is no string ambiguity between an instant and a relative expression. `width` sizes the **whole trigger** (number = px, string = CSS length verbatim), overriding the precision-derived default with no conversion in core; the two date zones stay equally split and elide when too narrow. Without `presets` there is no shortcut column or rule; without `width` the trigger keeps its precision-derived default. Both end boxes now use the core `Button` skin, so the range popup matches the rest of the kit.',
|
|
541
|
+
},
|
|
528
542
|
],
|
|
529
543
|
api: [
|
|
530
544
|
{
|
|
@@ -536,6 +550,8 @@ const doc: DocTopic = {
|
|
|
536
550
|
{ name: 'onChange', type: '(v: string | [string, string] | null) => void', def: '—', desc: { zh: '「确定」时提交;「清除」发 null', en: 'Emitted on confirm; clear emits null' } },
|
|
537
551
|
{ name: 'disabled / size / placeholder', type: "boolean / 'sm' | 'md' | 'lg' / string", def: "false / 'md' / 内置「请选择日期时间」", desc: { zh: '与 Picker 同名参数对齐', en: 'Aligned with Picker’s same-named params' } },
|
|
538
552
|
{ name: 'hint / className / ariaLabel / translate', type: 'string / string / string / Translate', def: '—', desc: { zh: '同 Picker 同名参数', en: 'Same-named params as Picker' } },
|
|
553
|
+
{ name: 'presets', type: 'DateTimePreset[]({ label: string | { zh, en }; expr: string | null })', def: '—', desc: { zh: 'range 专用的弹层右侧竖列;列高严格等于左侧日历区,档位多了在列内滚动;label 给双语对象即随 setLocale 切换,**英文建议缩写**(如 `This Q`)以免撑宽弹层;expr 走 date math 子集(Q 季 / y 年),null = 清空;不传 = 无入口', en: 'Range-only right-hand shortcut column; its height equals the left calendar zone and extra items scroll inside the column; a bilingual label follows setLocale — **prefer short English labels** (e.g. `This Q`) so the popup stays narrow; expr uses the date math subset (Q quarter / y year), null clears; omitted = no entry' } },
|
|
554
|
+
{ name: 'width', type: 'number | string', def: '按 precision 的默认宽', desc: { zh: '整个触发器总宽:数字 px / 字符串原样;覆盖默认宽、不做换算,过窄省略', en: 'Total trigger width: number = px, string = verbatim CSS length; overrides the default with no conversion, elides when narrow' } },
|
|
539
555
|
],
|
|
540
556
|
},
|
|
541
557
|
],
|
package/docs/help/ui.md
CHANGED
|
@@ -94,6 +94,10 @@ Picker:单 / 多选、上限、搜索、分组、空态,自绘。键盘:Es
|
|
|
94
94
|
|
|
95
95
|
> EN: Picker: single/multiple selection, cap, search, grouping, empty state — self-drawn. Keyboard: Esc closes, ↑↓ move the cursor, Enter selects. Logic reusable standalone: `toGroups` / `filterGroups` / `toggleValue`.
|
|
96
96
|
|
|
97
|
+
首屏分页:`limit` 按**选项条数**跨组切片,列表末尾「显示更多」(跟在最后一条后,像多一条选项)每点一次再追加 `limit` 条;搜索结果走同一套分页,换搜索词或重新展开都回到首屏。不传 `limit` 就是全量渲染。`PickerOption.keywords` 是可选的单个字符串(调用方自己拼),只和 `label` 拼起来参与大小写不敏感子串匹配,**绝不渲染**——适合任务 UUID 这种「用户能贴、但不该显示」的搜索词。纯逻辑另可单用 `sliceGroups`。
|
|
98
|
+
|
|
99
|
+
> EN: First-page paging: `limit` slices by **option count** across groups; "Show more" rendered as the last row of the list appends another `limit` each click. Search results use the same paging and reset to the first page on a new query or reopen. No `limit` means render everything. `PickerOption.keywords` is an optional single string (joined by the caller) that participates in case-insensitive substring matching together with `label` but is **never rendered** — for search terms like a task UUID that users paste but should not see. `sliceGroups` is also exported as pure logic.
|
|
100
|
+
|
|
97
101
|
WorkspacePicker:数据自带的领域选择器。在插件 apply 里调一次 `connectHost(ctx)`,组件即订阅官方 `workspaces` 快照。未接线 = 空态,不编造数据。
|
|
98
102
|
|
|
99
103
|
> EN: WorkspacePicker: a domain picker that brings its own data. Call `connectHost(ctx)` once in the plugin apply and it subscribes to the official `workspaces` snapshot. Unwired = empty state, never fabricated data.
|
|
@@ -110,6 +114,8 @@ ModelPicker:数据自带,`variant` 为 `grouped`(默认)/ `cascade`;`o
|
|
|
110
114
|
| `mode` | `'single' \| 'multiple'` | 'single' | — | — |
|
|
111
115
|
| `max` | `number` | — | 多选上限;到上限其余项置灰 + 提示(不静默截断) | Multiple-select cap; at cap others grey out + a hint (no silent truncation) |
|
|
112
116
|
| `searchable` | `boolean` | 'false' | 显示搜索框;展开菜单时自动聚焦搜索框 | Shows a search box; auto-focuses it when the menu opens |
|
|
117
|
+
| `limit` | `number` | — | 首屏显示的选项条数(跨组计);列表末尾「显示更多」每次再追加同样条数,搜索结果同样分页。不传 = 全量 | Options shown on the first page (counted across groups); "Show more" appends the same amount each click, search results included. Omitted = all |
|
|
118
|
+
| `options[].keywords` | `string` | — | 只参与搜索、不渲染的额外关键词(单个字符串,调用方自行拼接) | Extra search-only keyword, never rendered (one string, joined by the caller) |
|
|
113
119
|
| `align` | `'left' \| 'center'` | 'left' | 触发器文字对齐 | Trigger text alignment |
|
|
114
120
|
| `clearable` | `boolean` | 'false' | 单选时列表顶部多一项「不选」 | In single mode, adds a "none" item at the top of the list |
|
|
115
121
|
| `placeholder` | `string` | 内置「请选择」 | — | — |
|
|
@@ -267,6 +273,14 @@ h(InputGroup, {
|
|
|
267
273
|
|
|
268
274
|
> EN: The popup shares Picker’s mechanics (floating-ui layer, edge flip, outside-click close, no mask): two end boxes + two independent calendars (left = start, right = end, each picks its own, no span marking) + per-end hour/minute/second steppers (▲▼, direct typing, ↑↓ keys — no clicking through 60 minutes; **time never requires picking a date first** — on confirm, no date means no data emitted). Quick button: "Today" at day precision (date only), "Now" when hours/minutes/seconds are on (sets date + time together, editable afterwards). Draft state; onChange only on confirm; Esc / outside = discard. Calendar keys: arrows move, PageUp/Down change month, Home/End jump to month bounds, Enter selects. When end precedes start, confirm greys out with a hint.
|
|
269
275
|
|
|
276
|
+
`presets` 只在 range 生效:弹层拆成左区(两个端框 + 两块日历,同宽、上下对齐)与右侧一整列快捷按钮(从端框那行竖排到日历底部,一条竖线隔开,按钮多了自己滚)。**列宽固定容下英文缩写最长那档**(中文两字居中),列高严格等于左侧日历区,档位多了只在列内滚动、不把弹层撑高;按钮文字居中。快捷按钮(`size="sm"`)与两个端框(`size="md"`)**都复用核心 `Button`**(选中态走 `color` 轴),不另写皮肤、圆角与全站一致;`label` 由调用方给,给 `{ zh, en }` 双语对象即随 `setLocale` 自动切换(核心不内置文案)。`expr` 用 Grafana / Elasticsearch 的 date math 子集——`now/d` 今天、`now-1d/d` 昨天、`now/w` 本周、`now-1w/w` 上周、`now/M` 本月、`now-1M/M` 上月、`now/Q` 本季度、`now-1Q/Q` 上季度、`now/y` 今年、`now-1y/y` 去年(`Q` 季、`M` 月、`m` 分),`expr: null` 即清空起止。点一下**立刻 onChange 且面板不关**,当场看得见两端与日历一起变。选中态只按当前起止与各项算出的起止逐一比对:撞上就亮,一个没撞上就整排不亮(不显示「自定义」);每次渲染按当天重算,不用记状态。
|
|
277
|
+
|
|
278
|
+
> EN: `presets` applies to range only: the popup splits into a left zone (two end boxes + two calendars, same width and aligned) and one full-height shortcut column on the right (runs from the end-box row down past the calendars, separated by a vertical rule, scrolls when crowded). **Column width is fixed to fit the longest short English label** (two Chinese characters sit centered), its height exactly matches the left calendar zone, and extra items scroll inside the column without growing the popup; button text is centered. Shortcut buttons (`size="sm"`) and both end boxes (`size="md"`) **reuse the core `Button`** (active via the `color` axis) — no local skin, corner radius matches the rest of the kit; labels come from the caller, and a `{ zh, en }` object switches with `setLocale` (no built-in copy). `expr` uses the Grafana / Elasticsearch date math subset — `now/d` today, `now-1d/d` yesterday, `now/w` this week, `now-1w/w` last week, `now/M` this month, `now-1M/M` last month, `now/Q` this quarter, `now-1Q/Q` last quarter, `now/y` this year, `now-1y/y` last year (`Q` quarter, `M` month, `m` minute); `expr: null` clears both ends. A click **emits immediately without closing the popup**, so both ends and the calendars visibly jump together. The active state is pure value comparison against each preset’s resolved range: matches light up, no match leaves the whole column unlit (no "Custom" item). Ranges are recomputed against the current day on every render; no state is kept.
|
|
279
|
+
|
|
280
|
+
`value` 永远只放具体日期:短词只是「怎么算出这两个日期」的配方,不进值(与 Ant Design `presets` / MUI X `shortcuts` 同口径),因此不存在「这是时间点还是相对表达式」的字符串歧义。`width` 覆盖**整个触发器**(数字按 px、字符串按 CSS 长度原样),直接盖掉按 precision 写死的默认宽,核心不换算;两个日期区仍平分,过窄自动省略。不传 `presets` 就没有右侧快捷列与竖线;不传 `width` 时触发器仍按 precision 走默认宽。两个端框现已统一到核心 `Button` 皮肤,range 弹层外观与全站一致。
|
|
281
|
+
|
|
282
|
+
> EN: `value` always carries concrete dates only: the short expression is merely the recipe for computing those two dates and never enters the value (same shape as Ant Design `presets` / MUI X `shortcuts`), so there is no string ambiguity between an instant and a relative expression. `width` sizes the **whole trigger** (number = px, string = CSS length verbatim), overriding the precision-derived default with no conversion in core; the two date zones stay equally split and elide when too narrow. Without `presets` there is no shortcut column or rule; without `width` the trigger keeps its precision-derived default. Both end boxes now use the core `Button` skin, so the range popup matches the rest of the kit.
|
|
283
|
+
|
|
270
284
|
| 参数 | 类型 | 默认 | 说明(zh) | 说明(en) |
|
|
271
285
|
|---|---|---|---|---|
|
|
272
286
|
| `mode` | `'single' \| 'range'` | 'single' | single = 一个时刻;range = 一个整条触发器(两个日期区 + 灰隔条) | single = one instant; range = one-bar trigger (two date zones + grey separator) |
|
|
@@ -275,6 +289,8 @@ h(InputGroup, {
|
|
|
275
289
|
| `onChange` | `(v: string \| [string, string] \| null) => void` | — | 「确定」时提交;「清除」发 null | Emitted on confirm; clear emits null |
|
|
276
290
|
| `disabled / size / placeholder` | `boolean / 'sm' \| 'md' \| 'lg' / string` | false / 'md' / 内置「请选择日期时间」 | 与 Picker 同名参数对齐 | Aligned with Picker’s same-named params |
|
|
277
291
|
| `hint / className / ariaLabel / translate` | `string / string / string / Translate` | — | 同 Picker 同名参数 | Same-named params as Picker |
|
|
292
|
+
| `presets` | `DateTimePreset[]({ label: string \| { zh, en }; expr: string \| null })` | — | range 专用的弹层右侧竖列;列高严格等于左侧日历区,档位多了在列内滚动;label 给双语对象即随 setLocale 切换,**英文建议缩写**(如 `This Q`)以免撑宽弹层;expr 走 date math 子集(Q 季 / y 年),null = 清空;不传 = 无入口 | Range-only right-hand shortcut column; its height equals the left calendar zone and extra items scroll inside the column; a bilingual label follows setLocale — **prefer short English labels** (e.g. `This Q`) so the popup stays narrow; expr uses the date math subset (Q quarter / y year), null clears; omitted = no entry |
|
|
293
|
+
| `width` | `number \| string` | 按 precision 的默认宽 | 整个触发器总宽:数字 px / 字符串原样;覆盖默认宽、不做换算,过窄省略 | Total trigger width: number = px, string = verbatim CSS length; overrides the default with no conversion, elides when narrow |
|
|
278
294
|
|
|
279
295
|
```tsx id="ui-datetimepicker-example"
|
|
280
296
|
import { DateTimePicker } from 'dsh-plugin-core/ui'
|
package/package.json
CHANGED