@icen.ai/ui 0.8.0 → 0.9.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.
Files changed (228) hide show
  1. package/AGENTS.md +63 -14
  2. package/README.md +17 -9
  3. package/dist/base.css +4 -3
  4. package/dist/behaviors/accordion.d.mts +5 -2
  5. package/dist/behaviors/accordion.mjs +36 -11
  6. package/dist/behaviors/ai-branch.d.mts +19 -0
  7. package/dist/behaviors/ai-branch.mjs +217 -0
  8. package/dist/behaviors/ai-chat.d.mts +11 -7
  9. package/dist/behaviors/ai-chat.mjs +134 -51
  10. package/dist/behaviors/ai-composer.d.mts +28 -3
  11. package/dist/behaviors/ai-composer.mjs +399 -229
  12. package/dist/behaviors/ai-core.d.mts +9 -4
  13. package/dist/behaviors/ai-core.mjs +21 -8
  14. package/dist/behaviors/ai-diff.d.mts +2 -2
  15. package/dist/behaviors/ai-diff.mjs +172 -41
  16. package/dist/behaviors/ai-feedback.d.mts +26 -0
  17. package/dist/behaviors/ai-feedback.mjs +303 -0
  18. package/dist/behaviors/ai-panel.d.mts +26 -9
  19. package/dist/behaviors/ai-panel.mjs +211 -110
  20. package/dist/behaviors/ai-provider.mjs +54 -48
  21. package/dist/behaviors/ai-threads.d.mts +38 -0
  22. package/dist/behaviors/ai-threads.mjs +319 -0
  23. package/dist/behaviors/ai-tool.d.mts +12 -4
  24. package/dist/behaviors/ai-tool.mjs +316 -263
  25. package/dist/behaviors/ai-tools.d.mts +9 -6
  26. package/dist/behaviors/ai-tools.mjs +264 -155
  27. package/dist/behaviors/back-top.mjs +16 -1
  28. package/dist/behaviors/carousel.d.mts +5 -2
  29. package/dist/behaviors/carousel.mjs +53 -26
  30. package/dist/behaviors/charts.d.mts +170 -22
  31. package/dist/behaviors/charts.mjs +1016 -150
  32. package/dist/behaviors/command-palette.d.mts +40 -6
  33. package/dist/behaviors/command-palette.mjs +143 -15
  34. package/dist/behaviors/context-menu.d.mts +21 -4
  35. package/dist/behaviors/context-menu.mjs +122 -38
  36. package/dist/behaviors/controls.d.mts +59 -0
  37. package/dist/behaviors/controls.mjs +601 -0
  38. package/dist/behaviors/copy.d.mts +5 -2
  39. package/dist/behaviors/copy.mjs +37 -12
  40. package/dist/behaviors/datatable.d.mts +12 -0
  41. package/dist/behaviors/datatable.mjs +226 -43
  42. package/dist/behaviors/date-picker.d.mts +2 -2
  43. package/dist/behaviors/date-picker.mjs +171 -32
  44. package/dist/behaviors/dropdown.d.mts +6 -2
  45. package/dist/behaviors/dropdown.mjs +164 -25
  46. package/dist/behaviors/events.d.mts +523 -0
  47. package/dist/behaviors/events.mjs +208 -0
  48. package/dist/behaviors/input.mjs +25 -12
  49. package/dist/behaviors/kb-access.d.mts +34 -0
  50. package/dist/behaviors/kb-access.mjs +569 -0
  51. package/dist/behaviors/kb-acl.d.mts +50 -0
  52. package/dist/behaviors/kb-acl.mjs +405 -0
  53. package/dist/behaviors/kb-answer.d.mts +38 -0
  54. package/dist/behaviors/kb-answer.mjs +2055 -0
  55. package/dist/behaviors/kb-audit.d.mts +23 -0
  56. package/dist/behaviors/kb-audit.mjs +375 -0
  57. package/dist/behaviors/kb-canvas.d.mts +44 -0
  58. package/dist/behaviors/kb-canvas.mjs +589 -0
  59. package/dist/behaviors/kb-chain.d.mts +15 -0
  60. package/dist/behaviors/kb-chain.mjs +270 -0
  61. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  62. package/dist/behaviors/kb-checkpoint.mjs +342 -0
  63. package/dist/behaviors/kb-chunks.d.mts +31 -0
  64. package/dist/behaviors/kb-chunks.mjs +475 -0
  65. package/dist/behaviors/kb-citation.d.mts +38 -0
  66. package/dist/behaviors/kb-citation.mjs +677 -0
  67. package/dist/behaviors/kb-clarify.d.mts +20 -0
  68. package/dist/behaviors/kb-clarify.mjs +248 -0
  69. package/dist/behaviors/kb-conflict.d.mts +22 -0
  70. package/dist/behaviors/kb-conflict.mjs +284 -0
  71. package/dist/behaviors/kb-connector.d.mts +17 -0
  72. package/dist/behaviors/kb-connector.mjs +375 -0
  73. package/dist/behaviors/kb-core.d.mts +529 -0
  74. package/dist/behaviors/kb-core.mjs +890 -0
  75. package/dist/behaviors/kb-eval.d.mts +22 -0
  76. package/dist/behaviors/kb-eval.mjs +408 -0
  77. package/dist/behaviors/kb-explain.d.mts +11 -0
  78. package/dist/behaviors/kb-explain.mjs +243 -0
  79. package/dist/behaviors/kb-filter.d.mts +31 -0
  80. package/dist/behaviors/kb-filter.mjs +587 -0
  81. package/dist/behaviors/kb-gap.d.mts +27 -0
  82. package/dist/behaviors/kb-gap.mjs +273 -0
  83. package/dist/behaviors/kb-hittest.d.mts +34 -0
  84. package/dist/behaviors/kb-hittest.mjs +409 -0
  85. package/dist/behaviors/kb-metadata.d.mts +29 -0
  86. package/dist/behaviors/kb-metadata.mjs +326 -0
  87. package/dist/behaviors/kb-passage.d.mts +41 -0
  88. package/dist/behaviors/kb-passage.mjs +529 -0
  89. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  90. package/dist/behaviors/kb-pipeline.mjs +381 -0
  91. package/dist/behaviors/kb-qa.d.mts +26 -0
  92. package/dist/behaviors/kb-qa.mjs +332 -0
  93. package/dist/behaviors/kb-rerank.d.mts +20 -0
  94. package/dist/behaviors/kb-rerank.mjs +315 -0
  95. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  96. package/dist/behaviors/kb-retrieval.mjs +746 -0
  97. package/dist/behaviors/kb-review.d.mts +44 -0
  98. package/dist/behaviors/kb-review.mjs +451 -0
  99. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  100. package/dist/behaviors/kb-sandbox.mjs +325 -0
  101. package/dist/behaviors/kb-segment.d.mts +32 -0
  102. package/dist/behaviors/kb-segment.mjs +499 -0
  103. package/dist/behaviors/kb-sources.d.mts +27 -0
  104. package/dist/behaviors/kb-sources.mjs +373 -0
  105. package/dist/behaviors/kb-sql.d.mts +43 -0
  106. package/dist/behaviors/kb-sql.mjs +948 -0
  107. package/dist/behaviors/kb-trace.d.mts +16 -0
  108. package/dist/behaviors/kb-trace.mjs +414 -0
  109. package/dist/behaviors/kb-visibility.d.mts +44 -0
  110. package/dist/behaviors/kb-visibility.mjs +403 -0
  111. package/dist/behaviors/kb-who-can.d.mts +32 -0
  112. package/dist/behaviors/kb-who-can.mjs +379 -0
  113. package/dist/behaviors/modal.d.mts +24 -5
  114. package/dist/behaviors/modal.mjs +76 -10
  115. package/dist/behaviors/nav.d.mts +1 -1
  116. package/dist/behaviors/nav.mjs +33 -7
  117. package/dist/behaviors/notification.d.mts +20 -4
  118. package/dist/behaviors/notification.mjs +53 -73
  119. package/dist/behaviors/popover.d.mts +39 -3
  120. package/dist/behaviors/popover.mjs +90 -4
  121. package/dist/behaviors/select.d.mts +4 -2
  122. package/dist/behaviors/select.mjs +161 -24
  123. package/dist/behaviors/sidebar.d.mts +5 -2
  124. package/dist/behaviors/sidebar.mjs +43 -13
  125. package/dist/behaviors/slider.d.mts +5 -2
  126. package/dist/behaviors/slider.mjs +54 -43
  127. package/dist/behaviors/split-pane.d.mts +6 -2
  128. package/dist/behaviors/split-pane.mjs +62 -21
  129. package/dist/behaviors/tabs.d.mts +3 -2
  130. package/dist/behaviors/tabs.mjs +65 -14
  131. package/dist/behaviors/tag-input.d.mts +5 -2
  132. package/dist/behaviors/tag-input.mjs +61 -21
  133. package/dist/behaviors/theme.d.mts +13 -2
  134. package/dist/behaviors/theme.mjs +48 -4
  135. package/dist/behaviors/toast.d.mts +30 -8
  136. package/dist/behaviors/toast.mjs +74 -7
  137. package/dist/behaviors/tree.d.mts +5 -2
  138. package/dist/behaviors/tree.mjs +38 -15
  139. package/dist/behaviors/upload.mjs +21 -23
  140. package/dist/cli.mjs +1 -1
  141. package/dist/components/ai-branch.mjs +3 -0
  142. package/dist/components/ai-chat.css +295 -0
  143. package/dist/components/ai-feedback.mjs +3 -0
  144. package/dist/components/ai-panel.css +218 -1
  145. package/dist/components/ai-threads.mjs +3 -0
  146. package/dist/components/ai-tool-call.mjs +1 -1
  147. package/dist/components/badge.css +1 -1
  148. package/dist/components/breadcrumb.css +0 -32
  149. package/dist/components/chart-calendar.mjs +1 -2
  150. package/dist/components/chart-graph.css +57 -0
  151. package/dist/components/chart-graph.mjs +4 -0
  152. package/dist/components/chart-map.mjs +3 -0
  153. package/dist/components/chart-pie.css +0 -6
  154. package/dist/components/chart-scatter.mjs +1 -1
  155. package/dist/components/charts.css +33 -1
  156. package/dist/components/charts.mjs +1 -0
  157. package/dist/components/content.css +3 -16
  158. package/dist/components/datatable.css +1 -1
  159. package/dist/components/date-picker.css +1 -1
  160. package/dist/components/kb-access.mjs +4 -0
  161. package/dist/components/kb-acl.mjs +4 -0
  162. package/dist/components/kb-agent.css +481 -0
  163. package/dist/components/kb-answer.mjs +4 -0
  164. package/dist/components/kb-audit.mjs +4 -0
  165. package/dist/components/kb-canvas.mjs +4 -0
  166. package/dist/components/kb-chain.mjs +4 -0
  167. package/dist/components/kb-checkpoint.mjs +4 -0
  168. package/dist/components/kb-chunks.mjs +4 -0
  169. package/dist/components/kb-citation.mjs +4 -0
  170. package/dist/components/kb-clarify.mjs +4 -0
  171. package/dist/components/kb-conflict.mjs +4 -0
  172. package/dist/components/kb-connector.mjs +4 -0
  173. package/dist/components/kb-data.css +649 -0
  174. package/dist/components/kb-eval.mjs +4 -0
  175. package/dist/components/kb-explain.mjs +4 -0
  176. package/dist/components/kb-filter.mjs +4 -0
  177. package/dist/components/kb-gap.mjs +4 -0
  178. package/dist/components/kb-ground.css +566 -0
  179. package/dist/components/kb-hittest.mjs +4 -0
  180. package/dist/components/kb-ingest.css +898 -0
  181. package/dist/components/kb-metadata.mjs +4 -0
  182. package/dist/components/kb-ops.css +548 -0
  183. package/dist/components/kb-passage.mjs +4 -0
  184. package/dist/components/kb-perm.css +444 -0
  185. package/dist/components/kb-pipeline.mjs +4 -0
  186. package/dist/components/kb-qa.mjs +4 -0
  187. package/dist/components/kb-rerank.mjs +4 -0
  188. package/dist/components/kb-retrieval.mjs +4 -0
  189. package/dist/components/kb-review.mjs +4 -0
  190. package/dist/components/kb-sandbox.mjs +4 -0
  191. package/dist/components/kb-search.css +940 -0
  192. package/dist/components/kb-segment.mjs +4 -0
  193. package/dist/components/kb-sources.mjs +4 -0
  194. package/dist/components/kb-sql.mjs +4 -0
  195. package/dist/components/kb-trace.mjs +4 -0
  196. package/dist/components/kb-visibility.mjs +4 -0
  197. package/dist/components/kb-who-can.mjs +4 -0
  198. package/dist/components/kb.css +252 -0
  199. package/dist/components/kb.mjs +9 -0
  200. package/dist/components/notification.css +2 -3
  201. package/dist/components/pagination.css +3 -1
  202. package/dist/components/pagination.mjs +1 -0
  203. package/dist/components/rating.mjs +1 -0
  204. package/dist/components/segmented.css +9 -1
  205. package/dist/components/segmented.mjs +1 -1
  206. package/dist/components/select.css +67 -4
  207. package/dist/components/steps.css +13 -3
  208. package/dist/components/steps.mjs +1 -0
  209. package/dist/components/switch.css +3 -1
  210. package/dist/components/switch.mjs +1 -0
  211. package/dist/components/table.mjs +1 -0
  212. package/dist/components/tabs.css +23 -0
  213. package/dist/components/tag-input.css +1 -6
  214. package/dist/components/upload.css +0 -15
  215. package/dist/index.d.mts +48 -11
  216. package/dist/index.mjs +13205 -3615
  217. package/dist/registry.json +366 -15
  218. package/dist/retro-effects.css +3 -16
  219. package/dist/tokens/retro-effects.css +3 -16
  220. package/dist/tokens/style-profiles.css +3 -2
  221. package/dist/tokens/typography.css +2 -0
  222. package/dist/tokens.css +5 -2
  223. package/dist/ui.css +5713 -267
  224. package/docs/research/2026-10-03-ai-kb-components.md +392 -0
  225. package/docs/research/2026-10-03-kb-permissions.md +992 -0
  226. package/docs/spec/ai-native.md +7 -4
  227. package/docs/spec/kb-family.md +443 -0
  228. package/package.json +2 -2
@@ -70,7 +70,10 @@ interface NotificationHandle {
70
70
  id: string;
71
71
  /** 当前 DOM 元素(可能已脱离 DOM) */
72
72
  el: HTMLElement;
73
- /** 关闭本条 */
73
+ /**
74
+ * 关闭本条。设计边界:dismiss 不可拦截(无 before-close 钩子,onClose 只能事后观察);
75
+ * 持久化走 localStorage 是当前设计边界(无网络/多端同步,配额或隐私模式受限时静默放弃)。
76
+ */
74
77
  dismiss(): void;
75
78
  /** 部分更新(title / description / progress / unread / actions / tag 等) */
76
79
  update(patch: NotificationUpdatePatch): void;
@@ -99,6 +102,10 @@ interface NotificationUpdatePatch {
99
102
  link?: string;
100
103
  linkLabel?: string;
101
104
  }
105
+ /**
106
+ * 通知的内部记录形态,同时是 get(id) / getAll() 的公开返回类型
107
+ * (导出以便消费方对返回值做类型注解)。
108
+ */
102
109
  interface NotificationRecord {
103
110
  id: string;
104
111
  title: string;
@@ -129,6 +136,10 @@ interface NotifyFn {
129
136
  info(title: string, opts?: NotificationOptions): NotificationHandle | null;
130
137
  progress(title: string, opts?: NotificationOptions): NotificationHandle | null;
131
138
  confirm(title: string, opts?: NotificationConfirmOptions): Promise<boolean>;
139
+ /**
140
+ * 关闭单条(id 缺省 = 全部)。设计边界:dismiss 不可拦截、持久化走 localStorage
141
+ * 是当前设计边界(详见 NotificationHandle.dismiss)。
142
+ */
132
143
  dismiss(id?: string): void;
133
144
  clear(): void;
134
145
  markRead(id?: string): void;
@@ -166,7 +177,12 @@ interface NotificationPushInput {
166
177
  */
167
178
  declare function createNotificationCenter(parent?: ParentNode, opts?: NotificationConfig): NotificationCenterHandle;
168
179
  declare const notify: NotifyFn;
169
- /** 委托初始化:建立默认容器 + 恢复持久化通知。幂等。 */
170
- declare function initNotification(): void;
180
+ /**
181
+ * 委托初始化:建立默认容器 + 恢复持久化通知。幂等。
182
+ * 返回销毁函数:移除本次恢复的持久化通知(连同 DOM 与 store 记录),默认容器已空时
183
+ * 一并回收,并复位幂等标记(可重新 init)。注意:销毁只回收 init 恢复的内容;
184
+ * notify() 逐条创建的通知不在回收范围(由宿主自行 dismiss),destroy 后 push 仍可用。
185
+ */
186
+ declare function initNotification(): () => void;
171
187
 
172
- export { type NotificationAction, type NotificationCenterHandle, type NotificationConfig, type NotificationConfirmOptions, type NotificationHandle, type NotificationKind, type NotificationOptions, type NotificationPushInput, type NotificationUpdatePatch, createNotificationCenter, initNotification, notify };
188
+ export { type NotificationAction, type NotificationCenterHandle, type NotificationConfig, type NotificationConfirmOptions, type NotificationHandle, type NotificationKind, type NotificationOptions, type NotificationPushInput, type NotificationRecord, type NotificationUpdatePatch, createNotificationCenter, initNotification, notify };
@@ -240,7 +240,7 @@ function buildDescription(text) {
240
240
  el.textContent = text;
241
241
  return el;
242
242
  }
243
- function buildActions(record, getHandle) {
243
+ function buildActions(record) {
244
244
  const actions = record.opts.actions?.length ? record.opts.actions : record.opts.actionLabel ? [{ label: record.opts.actionLabel, onClick: record.opts.onAction }] : null;
245
245
  if (!actions || actions.length === 0) return null;
246
246
  const wrap = document.createElement("div");
@@ -254,13 +254,11 @@ function buildActions(record, getHandle) {
254
254
  btn.textContent = a.label;
255
255
  btn.addEventListener("click", (ev) => {
256
256
  ev.stopPropagation();
257
- const h = getHandle();
258
257
  if (a.onClick) {
259
258
  const ret = a.onClick();
260
259
  if (ret === false) return;
261
260
  }
262
261
  if (!a.keepOpen) dismissRecord(record.id);
263
- else void h;
264
262
  });
265
263
  wrap.appendChild(btn);
266
264
  }
@@ -379,7 +377,7 @@ function renderNotification(record, container, getHandle) {
379
377
  content.className = "notification-content";
380
378
  content.appendChild(buildTitle(record.title));
381
379
  if (record.opts.description) content.appendChild(buildDescription(record.opts.description));
382
- const actions = buildActions(record, getHandle);
380
+ const actions = buildActions(record);
383
381
  if (actions) content.appendChild(actions);
384
382
  const link = buildLink(record);
385
383
  if (link) content.appendChild(link);
@@ -415,6 +413,7 @@ function pushToContainer(container, el, priority) {
415
413
  container.appendChild(el);
416
414
  }
417
415
  }
416
+ var LEAVE_FALLBACK_MS = 360;
418
417
  function removeOne(el, onClose) {
419
418
  if (el.classList.contains("is-leaving")) return;
420
419
  el.classList.add("is-leaving");
@@ -426,23 +425,23 @@ function removeOne(el, onClose) {
426
425
  onClose?.();
427
426
  };
428
427
  el.addEventListener("transitionend", finish, { once: true });
429
- window.setTimeout(finish, 360);
428
+ window.setTimeout(finish, LEAVE_FALLBACK_MS);
430
429
  }
431
- function trimStack(container) {
430
+ function trimStack(container, maxStack, remove) {
432
431
  for (; ; ) {
433
432
  const items = Array.from(container.querySelectorAll(":scope > .notification"));
434
- if (items.length <= cfg.maxStack) return;
433
+ if (items.length <= maxStack) return;
435
434
  const target = items.find((it) => !it.classList.contains("is-priority")) ?? items[0];
436
435
  if (!target) return;
437
436
  const rec = target.id ? store.get(target.id) : void 0;
438
437
  if (rec?.timer) window.clearTimeout(rec.timer);
439
438
  if (target.classList.contains("is-leaving")) {
440
439
  target.remove();
441
- if (target.id) store.delete(target.id);
440
+ if (target.id) remove(target.id);
442
441
  continue;
443
442
  }
444
443
  removeOne(target);
445
- if (target.id) store.delete(target.id);
444
+ if (target.id) remove(target.id);
446
445
  return;
447
446
  }
448
447
  }
@@ -516,38 +515,49 @@ function makeHandle(record) {
516
515
  };
517
516
  return handle;
518
517
  }
519
- function showRecord(title, kind, opts) {
520
- if (!isBrowser()) return null;
521
- const container = ensureContainer();
518
+ function pushRecord(title, kind, opts, container, maxStack, cfgRef, ownerStore) {
522
519
  const id = opts.id ?? genId();
523
- if (store.has(id)) dismissRecord(id);
524
520
  const duration = opts.duration ?? 0;
525
521
  const record = {
526
522
  id,
527
523
  title,
528
524
  kind,
529
- opts: { unread: opts.unread ?? false, ...opts, id },
525
+ // 默认值放展开前会被显式传入的 { unread: undefined } 覆盖回 undefined,故先展开再兜底
526
+ opts: { ...opts, unread: opts.unread ?? false, id },
530
527
  el: null,
531
528
  createdAt: Date.now(),
532
529
  duration,
533
530
  remaining: duration,
534
531
  paused: false,
535
532
  timer: 0,
536
- startedAt: 0
533
+ startedAt: 0,
534
+ cfgRef,
535
+ ownerStore
537
536
  };
538
537
  const handle = makeHandle(record);
539
538
  const el = renderNotification(record, container, () => handle);
540
539
  record.el = el;
541
540
  handle.el = el;
542
541
  pushToContainer(container, el, record.opts.priority ?? "normal");
542
+ ownerStore?.set(id, record);
543
543
  store.set(id, record);
544
544
  attachTimer(record, handle);
545
- trimStack(container);
545
+ trimStack(container, maxStack, (removedId) => {
546
+ ownerStore?.delete(removedId);
547
+ store.delete(removedId);
548
+ });
546
549
  persistSave();
547
- playSound(record.cfgRef?.sound ?? cfg.sound);
550
+ playSound(cfgRef?.sound ?? cfg.sound);
548
551
  opts.onShow?.(handle);
549
552
  return handle;
550
553
  }
554
+ function showRecord(title, kind, opts) {
555
+ if (!isBrowser()) return null;
556
+ const container = ensureContainer();
557
+ const id = opts.id ?? genId();
558
+ if (store.has(id)) dismissRecord(id);
559
+ return pushRecord(title, kind, opts, container, cfg.maxStack);
560
+ }
551
561
  function dismissRecord(id) {
552
562
  const record = store.get(id);
553
563
  if (!record) {
@@ -696,6 +706,7 @@ function createNotificationCenter(parent, opts = {}) {
696
706
  getAll: () => [],
697
707
  config() {
698
708
  },
709
+ // SSR 桩:非真实元素,destroy 前不得触碰 el(本分支内其余方法均为 no-op)
699
710
  el: {}
700
711
  };
701
712
  }
@@ -726,60 +737,15 @@ function createNotificationCenter(parent, opts = {}) {
726
737
  }
727
738
  function push(input) {
728
739
  if (destroyed) return null;
729
- const kind = input.kind ?? "info";
730
- const opts2 = input.options ?? {};
731
- const id = opts2.id ?? genId();
732
- const duration = opts2.duration ?? 0;
733
- const record = {
734
- id,
735
- title: input.title,
736
- kind,
737
- opts: { unread: opts2.unread ?? false, ...opts2, id },
738
- el: null,
739
- createdAt: Date.now(),
740
- duration,
741
- remaining: duration,
742
- paused: false,
743
- timer: 0,
744
- startedAt: 0,
745
- cfgRef: local,
746
- ownerStore: localStore
747
- };
748
- const handle = makeHandle(record);
749
- const el = renderNotification(record, container, () => handle);
750
- record.el = el;
751
- handle.el = el;
752
- pushToContainer(container, el, record.opts.priority ?? "normal");
753
- localStore.set(id, record);
754
- store.set(id, record);
755
- attachTimer(record, handle);
756
- for (; ; ) {
757
- const items = Array.from(container.querySelectorAll(":scope > .notification"));
758
- if (items.length <= local.maxStack) break;
759
- const target = items.find((it) => !it.classList.contains("is-priority")) ?? items[0];
760
- if (!target) break;
761
- const tid = target.id;
762
- const rec = tid ? localStore.get(tid) : void 0;
763
- if (rec?.timer) window.clearTimeout(rec.timer);
764
- if (target.classList.contains("is-leaving")) {
765
- target.remove();
766
- if (tid) {
767
- localStore.delete(tid);
768
- store.delete(tid);
769
- }
770
- continue;
771
- }
772
- removeOne(target);
773
- if (tid) {
774
- localStore.delete(tid);
775
- store.delete(tid);
776
- }
777
- break;
778
- }
779
- persistSave();
780
- playSound(local.sound);
781
- opts2.onShow?.(handle);
782
- return handle;
740
+ return pushRecord(
741
+ input.title,
742
+ input.kind ?? "info",
743
+ input.options ?? {},
744
+ container,
745
+ local.maxStack,
746
+ local,
747
+ localStore
748
+ );
783
749
  }
784
750
  function dismiss(id) {
785
751
  if (id) {
@@ -889,17 +855,31 @@ var notify = Object.assign(
889
855
  }
890
856
  );
891
857
  function initNotification() {
892
- if (!isBrowser()) return;
893
- if (globalInit.done) return;
858
+ if (!isBrowser()) return () => {
859
+ };
860
+ if (globalInit.done) return () => {
861
+ };
894
862
  globalInit.done = true;
895
863
  const container = ensureContainer();
896
864
  const key = persistKeyFor(cfg.persist);
865
+ const restored = [];
897
866
  if (key) {
898
867
  knownPersistKeys.add(key);
899
868
  for (const r of restorePersisted(container, key)) {
900
869
  store.set(r.id, r);
870
+ restored.push(r);
901
871
  }
902
872
  }
873
+ return () => {
874
+ for (const r of restored) {
875
+ if (r.timer) window.clearTimeout(r.timer);
876
+ store.delete(r.id);
877
+ r.el?.remove();
878
+ }
879
+ restored.length = 0;
880
+ if (container.childElementCount === 0) container.remove();
881
+ globalInit.done = false;
882
+ };
903
883
  }
904
884
  export {
905
885
  createNotificationCenter,
@@ -1,3 +1,4 @@
1
+ /** 布局锚矩形(视口坐标,宽高齐全,元素锚或手工锚共用此形状)。 */
1
2
  interface PopoverRect {
2
3
  left: number;
3
4
  top: number;
@@ -6,11 +7,14 @@ interface PopoverRect {
6
7
  width: number;
7
8
  height: number;
8
9
  }
10
+ /** 视口尺寸(可用空间计算的基准)。 */
9
11
  interface PopoverViewport {
10
12
  width: number;
11
13
  height: number;
12
14
  }
13
- type PopoverSide = 'top' | 'bottom';
15
+ /** 浮层相对锚的展开侧。 */
16
+ type PopoverSide = 'top' | 'bottom' | 'left' | 'right';
17
+ /** 浮层与锚的对齐方式(stretch = 纵向侧时随锚宽拉伸)。 */
14
18
  type PopoverAlign = 'start' | 'center' | 'end' | 'stretch';
15
19
  interface PopoverLayoutOptions {
16
20
  side?: PopoverSide;
@@ -24,6 +28,33 @@ interface PopoverLayoutOptions {
24
28
  /** 内容预期高度:避免用被裁剪的实测高度参与选侧 */
25
29
  contentHeightHint?: number;
26
30
  }
31
+ interface PanelSizing {
32
+ /** 固定宽(最高优先,跳过 min/max 夹取) */
33
+ width?: number;
34
+ /** 最小宽(默认 = 锚点/trigger 宽或组件 CSS 默认) */
35
+ minWidth?: number;
36
+ /** 最大宽 */
37
+ maxWidth?: number;
38
+ /** 最小高(与 PopoverLayoutOptions.minHeight 对齐) */
39
+ minHeight?: number;
40
+ /** 高度上限 */
41
+ maxHeight?: number;
42
+ }
43
+ /** 从元素读取 data-panel-* 尺寸属性(非法/缺失字段跳过,不抛异常)。 */
44
+ declare function readPanelSizing(el: Element | null | undefined): PanelSizing;
45
+ /** 合并尺寸:程序面 overrides 优先于属性面(undefined 字段不覆盖)。 */
46
+ declare function resolvePanelSizing(el: Element | null | undefined, overrides?: PanelSizing | null): PanelSizing;
47
+ /** 把 PanelSizing 折算进布局 opts:固定宽 → min=max;否则 min/max 夹取;maxHeight 透传。 */
48
+ declare function sizingToLayout<T extends {
49
+ minWidth?: number;
50
+ maxWidth?: number;
51
+ maxHeight?: number;
52
+ }>(sizing: PanelSizing, opts: T): T;
53
+ /** 把尺寸契约内联到自建面板(dropdown / context-menu / date-picker 等 fixed 面板)。
54
+ * 传 null 复位全部内联尺寸——单例面板跨实例复用时防「上次的设置串味」。
55
+ * 视口守卫:width / maxWidth 一律夹到 视口宽 − 24px(窄屏上过大的 data-panel-width 不溢出,
56
+ * 与 computePopoverLayout 的 availableWidth 同一纪律,不再两套标准)。 */
57
+ declare function applyPanelSizing(panel: HTMLElement, sizing: PanelSizing | null): void;
27
58
  interface PopoverLayout {
28
59
  side: PopoverSide;
29
60
  left: number;
@@ -33,14 +64,19 @@ interface PopoverLayout {
33
64
  }
34
65
  interface OpenPopoverOptions extends PopoverLayoutOptions {
35
66
  anchor: Element | PopoverRect;
67
+ /** 统一面板尺寸契约(优先于面板元素的 data-panel-* 与上方直接尺寸字段) */
68
+ sizing?: PanelSizing;
36
69
  /** 关闭(含外点/Esc 触发的关闭)后的回调 */
37
70
  onClose?: () => void;
38
71
  }
39
- /** 视口智能布局:返回 {side, left, top, width, maxHeight}。 */
72
+ /** 视口智能布局:返回 {side, left, top, width, maxHeight}。
73
+ * 纵向侧(top/bottom):选侧按上下空间,宽按 min/max 夹取,高按内容预期。
74
+ * 横向侧(left/right):锚点左右翻转,宽取 min(无内容测量,横向侧取下限),
75
+ * 高占满可用空间,纵向按 align(start/center/end;stretch 视作 start)贴锚。 */
40
76
  declare function computePopoverLayout(anchor: PopoverRect, viewport: PopoverViewport, opts?: PopoverLayoutOptions): PopoverLayout;
41
77
  /** 打开浮层:panel 移入 body 并按 anchor 布局;重复调用同一 panel 为先关后开。 */
42
78
  declare function openPopover(panel: HTMLElement, options: OpenPopoverOptions): void;
43
79
  /** 关闭浮层:移除监听并把 panel 还原到打开前的位置与 hidden 状态。 */
44
80
  declare function closePopover(panel: HTMLElement): void;
45
81
 
46
- export { type OpenPopoverOptions, type PopoverAlign, type PopoverLayout, type PopoverLayoutOptions, type PopoverRect, type PopoverSide, type PopoverViewport, closePopover, computePopoverLayout, openPopover };
82
+ export { type OpenPopoverOptions, type PanelSizing, type PopoverAlign, type PopoverLayout, type PopoverLayoutOptions, type PopoverRect, type PopoverSide, type PopoverViewport, applyPanelSizing, closePopover, computePopoverLayout, openPopover, readPanelSizing, resolvePanelSizing, sizingToLayout };
@@ -1,4 +1,63 @@
1
1
  // src/behaviors/popover.ts
2
+ var PANEL_SIZING_ATTRS = {
3
+ width: "data-panel-width",
4
+ minWidth: "data-panel-min",
5
+ maxWidth: "data-panel-max",
6
+ minHeight: "data-panel-min-height",
7
+ maxHeight: "data-panel-max-height"
8
+ };
9
+ function readPanelSizing(el) {
10
+ const out = {};
11
+ if (!el) return out;
12
+ for (const key of Object.keys(PANEL_SIZING_ATTRS)) {
13
+ const raw = el.getAttribute(PANEL_SIZING_ATTRS[key]);
14
+ if (raw == null) continue;
15
+ const n = Number(raw);
16
+ if (Number.isFinite(n) && n > 0) out[key] = n;
17
+ }
18
+ return out;
19
+ }
20
+ function resolvePanelSizing(el, overrides) {
21
+ const base = readPanelSizing(el);
22
+ if (!overrides) return base;
23
+ for (const key of Object.keys(overrides)) {
24
+ const v = overrides[key];
25
+ if (typeof v === "number" && Number.isFinite(v) && v > 0) base[key] = v;
26
+ }
27
+ return base;
28
+ }
29
+ function sizingToLayout(sizing, opts) {
30
+ const out = { ...opts };
31
+ if (sizing.minWidth != null) out.minWidth = sizing.minWidth;
32
+ if (sizing.maxWidth != null) out.maxWidth = sizing.maxWidth;
33
+ if (sizing.width != null) {
34
+ out.minWidth = sizing.width;
35
+ out.maxWidth = sizing.width;
36
+ }
37
+ if (sizing.maxHeight != null) out.maxHeight = sizing.maxHeight;
38
+ return out;
39
+ }
40
+ function applyPanelSizing(panel, sizing) {
41
+ if (sizing == null) {
42
+ panel.style.width = "";
43
+ panel.style.minWidth = "";
44
+ panel.style.maxWidth = "";
45
+ panel.style.minHeight = "";
46
+ panel.style.maxHeight = "";
47
+ return;
48
+ }
49
+ const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
50
+ const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
51
+ if (sizing.width != null) {
52
+ const w = fitW(sizing.width);
53
+ panel.style.width = `${w}px`;
54
+ panel.style.maxWidth = `${w}px`;
55
+ }
56
+ if (sizing.minWidth != null) panel.style.minWidth = `${fitW(sizing.minWidth)}px`;
57
+ if (sizing.maxWidth != null && sizing.width == null) panel.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
58
+ if (sizing.minHeight != null) panel.style.minHeight = `${sizing.minHeight}px`;
59
+ if (sizing.maxHeight != null) panel.style.maxHeight = `${sizing.maxHeight}px`;
60
+ }
2
61
  var DEFAULT_MIN_WIDTH = 220;
3
62
  var DEFAULT_MAX_WIDTH = 420;
4
63
  var DEFAULT_MIN_HEIGHT = 120;
@@ -23,13 +82,35 @@ function horizontalAnchor(anchor, width, align) {
23
82
  function computePopoverLayout(anchor, viewport, opts = {}) {
24
83
  const margin = opts.margin ?? 12;
25
84
  const offset = opts.offset ?? 8;
85
+ const preferredSide = opts.side ?? "top";
86
+ const align = opts.align ?? "start";
87
+ if (preferredSide === "left" || preferredSide === "right") {
88
+ const availableLeft = Math.max(0, anchor.left - margin - offset);
89
+ const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
90
+ const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
91
+ const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
92
+ const maxWidth2 = Math.min(
93
+ Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
94
+ side2 === "left" ? availableLeft : availableRight
95
+ );
96
+ const width2 = minWidth2;
97
+ const left2 = clamp(
98
+ side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
99
+ margin,
100
+ viewport.width - margin - width2
101
+ );
102
+ const availableHeight2 = Math.max(80, viewport.height - margin * 2);
103
+ const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
104
+ const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
105
+ const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
106
+ const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
107
+ return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
108
+ }
26
109
  const availableWidth = Math.max(80, viewport.width - margin * 2);
27
110
  const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
28
111
  const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
29
112
  const maxWidth = Math.min(requestedMaxWidth, availableWidth);
30
113
  const minWidth = Math.min(requestedMinWidth, maxWidth);
31
- const preferredSide = opts.side ?? "top";
32
- const align = opts.align ?? "start";
33
114
  const availableTop = Math.max(0, anchor.top - margin - offset);
34
115
  const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
35
116
  const availableHeight = Math.max(80, viewport.height - margin * 2);
@@ -107,7 +188,8 @@ function onKeyDown(e) {
107
188
  function openPopover(panel, options) {
108
189
  if (typeof document === "undefined") return;
109
190
  if (openPanels.has(panel)) closePopover(panel);
110
- const { anchor, onClose, ...layoutOpts } = options;
191
+ const { anchor, onClose, sizing, ...rest } = options;
192
+ const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
111
193
  const placeholder = document.createComment("popover-anchor");
112
194
  panel.parentNode?.insertBefore(placeholder, panel);
113
195
  const wasHidden = panel.hidden;
@@ -159,7 +241,11 @@ function closePopover(panel) {
159
241
  state.onClose?.();
160
242
  }
161
243
  export {
244
+ applyPanelSizing,
162
245
  closePopover,
163
246
  computePopoverLayout,
164
- openPopover
247
+ openPopover,
248
+ readPanelSizing,
249
+ resolvePanelSizing,
250
+ sizingToLayout
165
251
  };
@@ -1,4 +1,6 @@
1
- /** 为 root 下每个 [data-select] 容器初始化(root 自身是 [data-select] 也算)。 */
2
- declare function initSelect(root?: ParentNode): void;
1
+ /** 为 root 下每个 [data-select] 容器初始化(root 自身是 [data-select] 也算)。
2
+ * 返回销毁函数:摘除本次初始化容器的全部监听并复位幂等标记(可重新 init);
3
+ * 最后一个容器销毁时同时摘除 document 级单例委托。 */
4
+ declare function initSelect(root?: ParentNode): () => void;
3
5
 
4
6
  export { initSelect };