@heyramzi/ui 2.7.0 → 2.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 (132) hide show
  1. package/COMPONENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/package.json +58 -74
  4. package/src/agents/vendor/draw.ts +4 -4
  5. package/src/ai/generation-progress/GenerationProgress.svelte +2 -2
  6. package/src/animations/README.md +1 -1
  7. package/src/animations/card-tilt.css +1 -1
  8. package/src/animations/digit-pop-in.css +1 -1
  9. package/src/animations/input-clear-dissolve.css +2 -2
  10. package/src/animations/learn-more-hover.css +2 -2
  11. package/src/animations/like-button.css +1 -1
  12. package/src/animations/plus-menu-morph.css +1 -1
  13. package/src/animations/success-check.css +5 -5
  14. package/src/canvas/headless/canvas-cleanup.test.ts +1 -1
  15. package/src/canvas/ops/ops.ts +1 -1
  16. package/src/charts/area-chart.svelte +11 -1
  17. package/src/charts/bar-chart.css +2 -1
  18. package/src/charts/bar-chart.svelte +18 -3
  19. package/src/charts/charts.css +2 -1
  20. package/src/charts/line-chart.svelte +11 -1
  21. package/src/components/data-table/data-table.ts +4 -5
  22. package/src/components/data-table/pagination.svelte +5 -3
  23. package/src/components/docs/on-this-page.svelte +82 -5
  24. package/src/components/docs/scroll-spy.svelte.ts +6 -1
  25. package/src/components/list-page/detail-header.svelte +7 -3
  26. package/src/components/list-page/empty-state.svelte +4 -2
  27. package/src/components/list-page/filter-popover.svelte +2 -2
  28. package/src/components/list-page/rail-row.svelte +5 -2
  29. package/src/components/mark/mark.svelte +2 -1
  30. package/src/components/section-card/section-card.svelte +3 -1
  31. package/src/components/section-header/section-header.svelte +5 -5
  32. package/src/components/segmented/segment.svelte +1 -1
  33. package/src/components/table-view/table-view.svelte +6 -1
  34. package/src/components/tags-input/tags-input.svelte +1 -1
  35. package/src/components/toolbar/toolbar.svelte +1 -1
  36. package/src/design/index.css +7 -5
  37. package/src/editor/anchors.ts +28 -0
  38. package/src/editor/doc-blocks.ts +168 -0
  39. package/src/editor/doc-page.css +389 -0
  40. package/src/editor/editor.svelte +47 -5
  41. package/src/editor/extensions.ts +26 -2
  42. package/src/editor/markdown.ts +27 -2
  43. package/src/editor/slash-items.ts +18 -0
  44. package/src/effects/reveal/reveal.css +1 -1
  45. package/src/icons/trades.ts +1 -1
  46. package/src/inbox/README.md +6 -4
  47. package/src/inbox/inbox.test.ts +1 -1
  48. package/src/inbox/index.ts +2 -2
  49. package/src/inbox/logic.ts +29 -5
  50. package/src/inbox/thread-inbox.svelte +219 -92
  51. package/src/inbox/types.ts +27 -1
  52. package/src/lib/initials.test.ts +25 -0
  53. package/src/lib/initials.ts +29 -0
  54. package/src/lib/theme-transition.ts +6 -2
  55. package/src/modules.test.ts +4 -64
  56. package/src/panel/README.md +1 -1
  57. package/src/record/README.md +1 -1
  58. package/src/record/info-list.svelte +5 -4
  59. package/src/record/next-up.svelte +1 -1
  60. package/src/record/project-list.svelte +1 -1
  61. package/src/record/steps-rail.svelte +3 -3
  62. package/src/shadcn/badge/badge.svelte +1 -1
  63. package/src/shadcn/card/card-title.svelte +1 -5
  64. package/src/shadcn/empty/empty-description.svelte +1 -1
  65. package/src/shadcn/progress/progress.svelte +8 -1
  66. package/src/shadcn/table/table-cell.svelte +10 -2
  67. package/src/slides/theme.css +2 -2
  68. package/src/status/README.md +1 -1
  69. package/src/status/status-dot.svelte +3 -3
  70. package/src/thread/README.md +2 -0
  71. package/src/thread/index.ts +9 -1
  72. package/src/thread/logic.ts +33 -3
  73. package/src/thread/thread-body.svelte +37 -0
  74. package/src/thread/thread-events.svelte +30 -0
  75. package/src/thread/thread.svelte +74 -59
  76. package/src/thread/types.ts +21 -0
  77. package/src/tokens/craft.css +7 -5
  78. package/src/{tasks → work}/README.md +11 -8
  79. package/src/{tasks → work}/detail/task-thread.svelte +8 -15
  80. package/src/work/docs/doc-editor-page.svelte +194 -0
  81. package/src/work/docs/doc-menu.svelte +333 -0
  82. package/src/work/docs/docs-demo.svelte +227 -0
  83. package/src/work/docs/edited-chip.svelte +125 -0
  84. package/src/work/docs/edited.ts +54 -0
  85. package/src/work/docs/index.ts +38 -0
  86. package/src/work/docs/labels.ts +70 -0
  87. package/src/work/docs/menu.ts +127 -0
  88. package/src/work/docs/outline.ts +60 -0
  89. package/src/{tasks → work}/gantt/gantt-grid.svelte +2 -2
  90. package/src/{tasks → work}/gantt/gantt-group-row.svelte +1 -1
  91. package/src/{tasks → work}/gantt/gantt.css +1 -1
  92. package/src/{tasks → work}/inbox/inbox-card.svelte +113 -50
  93. package/src/work/inbox/inbox-logic.ts +98 -0
  94. package/src/work/inbox/inbox-trace.svelte +58 -0
  95. package/src/{tasks → work}/inbox/inbox-types.ts +2 -0
  96. package/src/{tasks → work}/index.ts +0 -10
  97. package/src/{tasks → work}/kanban-board.svelte +1 -1
  98. package/src/{tasks → work}/kanban-column.svelte +1 -1
  99. package/src/{tasks → work}/task-list.svelte +2 -2
  100. package/src/{tasks → work}/time/time-ring.svelte +1 -1
  101. package/src/{tasks → work}/time/time-tracker.svelte +3 -6
  102. package/src/tasks/inbox/inbox-logic.ts +0 -21
  103. package/src/tasks/inbox/inbox-trace.svelte +0 -51
  104. /package/src/{tasks → work}/ambient.d.ts +0 -0
  105. /package/src/{tasks → work}/board-order.ts +0 -0
  106. /package/src/{tasks → work}/dates.ts +0 -0
  107. /package/src/{tasks → work}/demo.svelte +0 -0
  108. /package/src/{tasks → work}/detail/detail-demo.svelte +0 -0
  109. /package/src/{tasks → work}/detail/detail-types.ts +0 -0
  110. /package/src/{tasks → work}/detail/task-chip.svelte +0 -0
  111. /package/src/{tasks → work}/detail/task-detail.svelte +0 -0
  112. /package/src/{tasks → work}/detail/task-property.svelte +0 -0
  113. /package/src/{tasks → work}/gantt/gantt-bar.svelte +0 -0
  114. /package/src/{tasks → work}/gantt/gantt-header.svelte +0 -0
  115. /package/src/{tasks → work}/gantt/gantt-task-row.svelte +0 -0
  116. /package/src/{tasks → work}/gantt/gantt.test.ts +0 -0
  117. /package/src/{tasks → work}/gantt/logic.test.ts +0 -0
  118. /package/src/{tasks → work}/gantt/logic.ts +0 -0
  119. /package/src/{tasks → work}/inbox/inbox-draft.svelte +0 -0
  120. /package/src/{tasks → work}/inbox/inbox-statuses.ts +0 -0
  121. /package/src/{tasks → work}/inbox/index.ts +0 -0
  122. /package/src/{tasks → work}/kanban-drag.ts +0 -0
  123. /package/src/{tasks → work}/kanban-types.ts +0 -0
  124. /package/src/{tasks → work}/presets/sales.ts +0 -0
  125. /package/src/{tasks → work}/reorder.ts +0 -0
  126. /package/src/{tasks → work}/task-gantt.svelte +0 -0
  127. /package/src/{tasks → work}/task-types.ts +0 -0
  128. /package/src/{tasks → work}/time/duration.ts +0 -0
  129. /package/src/{tasks → work}/time/time-chip.svelte +0 -0
  130. /package/src/{tasks → work}/time/time-dot.svelte +0 -0
  131. /package/src/{tasks → work}/time/time-types.ts +0 -0
  132. /package/src/{tasks → work}/use-kanban-dnd.svelte.ts +0 -0
@@ -0,0 +1,98 @@
1
+ import type { InboxDraft, InboxItem } from "./inbox-types.js";
2
+
3
+ /** The draft to open on: the agent's pick, else the first. */
4
+ export function startDraft(drafts: InboxDraft[], recommended?: string): InboxDraft | undefined {
5
+ return drafts.find((draft) => draft.templateId === recommended) ?? drafts[0];
6
+ }
7
+
8
+ /** A person can act on an item only while it waits for them. */
9
+ export function isOpen(item: InboxItem): boolean {
10
+ return item.tier === "ask" && item.state === "proposed";
11
+ }
12
+
13
+ export function canUndo(item: InboxItem): boolean {
14
+ return item.tier === "auto" && item.state === "done";
15
+ }
16
+
17
+ /** Lowercase, one space between words, no trailing ellipsis: what two copies of a line share. */
18
+ function plain(text: string): string {
19
+ return text
20
+ .replace(/(…|\.{3})\s*$/, "")
21
+ .replace(/\s+/g, " ")
22
+ .trim()
23
+ .toLowerCase();
24
+ }
25
+
26
+ /** The lines of a card that say something the title and the command don't. */
27
+ export type CardFacts = {
28
+ lines: string[];
29
+ command?: { tool: string; text: string };
30
+ };
31
+
32
+ /**
33
+ * Each fact once: a line that repeats the title or the command, even clipped, is dropped, and
34
+ * one that starts with the title keeps only the rest.
35
+ */
36
+ export function cardFacts(item: InboxItem): CardFacts {
37
+ const { command, summary, detail } = item.proposal;
38
+ const shown = [plain(item.title)];
39
+ if (command) shown.push(plain(`${command.tool}: ${command.text}`), plain(command.text));
40
+ const lines: string[] = [];
41
+ for (const raw of [summary, detail]) {
42
+ let line = raw?.trim() ?? "";
43
+ const title = item.title.trim();
44
+ if (line.toLowerCase().startsWith(title.toLowerCase()) && line.length > title.length) {
45
+ line = line.slice(title.length).replace(/^[\s:.,;-]+/, "");
46
+ }
47
+ const key = plain(line);
48
+ if (!key || shown.some((seen) => seen.includes(key) || key.includes(seen))) continue;
49
+ shown.push(key);
50
+ lines.push(line);
51
+ }
52
+ return { lines, command };
53
+ }
54
+
55
+ /** The reasoning of a trace when no line of the card already says it. */
56
+ export function freshReasoning(item: InboxItem, facts: CardFacts): string {
57
+ const reasoning = item.trace.reasoning.trim();
58
+ const key = plain(reasoning);
59
+ if (!key) return "";
60
+ const said = [item.title, item.proposal.summary, item.proposal.detail ?? "", ...facts.lines]
61
+ .map(plain)
62
+ .filter(Boolean);
63
+ return said.some((line) => line.includes(key) || key.includes(line)) ? "" : reasoning;
64
+ }
65
+
66
+ const ISO = /^\d{4}-\d{2}-\d{2}T/;
67
+ const UNITS: [number, Intl.RelativeTimeFormatUnit][] = [
68
+ [604_800, "week"],
69
+ [86_400, "day"],
70
+ [3_600, "hour"],
71
+ [60, "minute"],
72
+ ];
73
+
74
+ /**
75
+ * A time as a person says it: "3 hr. ago", "Oct 12". A host-formatted string stays as written,
76
+ * and a raw ISO string never reaches the screen.
77
+ */
78
+ export function whenOf(at: string, now: number = Date.now(), locale = "en-GB"): string {
79
+ if (!ISO.test(at)) return at;
80
+ const time = Date.parse(at);
81
+ if (Number.isNaN(time)) return "";
82
+ const seconds = Math.round((time - now) / 1000);
83
+ const age = Math.abs(seconds);
84
+ if (age >= 4 * 604_800) {
85
+ return new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" }).format(time);
86
+ }
87
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto", style: "short" });
88
+ for (const [size, unit] of UNITS) {
89
+ if (age >= size) return rtf.format(Math.round(seconds / size), unit);
90
+ }
91
+ return rtf.format(0, "second");
92
+ }
93
+
94
+ /** True when a key press belongs to a text field and so isn't a shortcut. */
95
+ export function isTyping(target: EventTarget | null): boolean {
96
+ if (!(target instanceof HTMLElement)) return false;
97
+ return target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
98
+ }
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ import { Reasoning, ReasoningContent, ReasoningTrigger } from "../../ai/reasoning/index.js";
3
+ import type { InboxTrace } from "./inbox-types.js";
4
+
5
+ interface Props {
6
+ trace: InboxTrace;
7
+ /** The reasoning no other line of the card says. Empty when the card already shows it. */
8
+ reasoning?: string;
9
+ /** The name the card's meta line shows, so the source label doesn't repeat it. */
10
+ agent?: string;
11
+ }
12
+
13
+ let { trace, reasoning = "", agent }: Props = $props();
14
+
15
+ let count = $derived(trace.evidence.length);
16
+ let label = $derived(
17
+ trace.source.label.trim() === agent?.trim() ? "" : trace.source.label.trim(),
18
+ );
19
+ </script>
20
+
21
+ <!-- WHY the shared Reasoning and not a local disclosure: it is already the
22
+ collapsed-by-default "how the agent got here" pattern, with the same chevron. -->
23
+ <Reasoning>
24
+ <ReasoningTrigger class="w-auto">
25
+ <span>Trace{count > 0 ? ` · ${count} ${count === 1 ? "quote" : "quotes"}` : ""}</span>
26
+ </ReasoningTrigger>
27
+ <ReasoningContent>
28
+ <div class="flex flex-col gap-3">
29
+ {#if label || trace.source.href}
30
+ <p class="text-foreground">
31
+ {label}
32
+ {#if trace.source.href}
33
+ <a href={trace.source.href} class="text-foreground underline underline-offset-2"
34
+ >Open source</a
35
+ >
36
+ {/if}
37
+ </p>
38
+ {/if}
39
+ {#if count > 0}
40
+ <ul class="flex flex-col gap-2">
41
+ {#each trace.evidence as evidence, i (i)}
42
+ <li>
43
+ <blockquote class="text-foreground">&ldquo;{evidence.quote}&rdquo;</blockquote>
44
+ {#if evidence.by || evidence.at}
45
+ <span class="text-xs tabular-nums"
46
+ >{[evidence.by, evidence.at].filter(Boolean).join(", ")}</span
47
+ >
48
+ {/if}
49
+ </li>
50
+ {/each}
51
+ </ul>
52
+ {/if}
53
+ {#if reasoning}
54
+ <p><span class="text-foreground font-medium">Why:</span> {reasoning}</p>
55
+ {/if}
56
+ </div>
57
+ </ReasoningContent>
58
+ </Reasoning>
@@ -28,6 +28,8 @@ export type InboxDraft = {
28
28
  export type InboxProposal = {
29
29
  summary: string;
30
30
  detail?: string;
31
+ /** The exact call to run, drawn once in monospace. Summary and detail that repeat it are dropped. */
32
+ command?: { tool: string; text: string };
31
33
  drafts?: InboxDraft[];
32
34
  /** The draft to open on. Defaults to the first. */
33
35
  recommended?: string;
@@ -13,16 +13,6 @@ export type {
13
13
  KanbanMove,
14
14
  KanbanSortKey,
15
15
  } from "./kanban-types.js";
16
- export { StatusBadge, StatusDot, StatusIcon, defineStatuses } from "../status/index.js";
17
- export type {
18
- StatusDef,
19
- StatusDotVariant,
20
- StatusId,
21
- StatusMap,
22
- Statuses,
23
- } from "../status/index.js";
24
- export { SidePanel } from "../panel/index.js";
25
- export type { PanelStat, SidePanelWidth } from "../panel/index.js";
26
16
  export { default as TaskList } from "./task-list.svelte";
27
17
  export { default as TaskGantt } from "./task-gantt.svelte";
28
18
  export { addDays, dayNumber, isoFromDay, todayIso } from "./dates.js";
@@ -484,7 +484,7 @@
484
484
  <span class="truncate whitespace-nowrap">{column.label}</span>
485
485
  {#if count > 0}
486
486
  <span class="ms-auto text-xs text-muted-foreground/60 tabular-nums"
487
- >{count}</span
487
+ >{count.toLocaleString()}</span
488
488
  >
489
489
  {/if}
490
490
  {/if}
@@ -93,7 +93,7 @@
93
93
  .kanban-column-expanding {
94
94
  flex: 0 0 280px;
95
95
  width: 280px;
96
- animation: column-expand var(--duration-slow, 320ms) var(--ease-smooth-out, ease-out) both;
96
+ animation: column-expand var(--duration-slow, 300ms) var(--ease-smooth-out, ease-out) both;
97
97
  }
98
98
 
99
99
  @keyframes column-expand {
@@ -254,7 +254,7 @@
254
254
  <span
255
255
  class="grid h-5 min-w-5 place-items-center rounded-sm bg-foreground/5 px-1 text-xs font-medium text-muted-foreground tabular-nums"
256
256
  >
257
- {count}
257
+ {count.toLocaleString()}
258
258
  </span>
259
259
  {/if}
260
260
  {#if actions}
@@ -408,7 +408,7 @@
408
408
  }
409
409
 
410
410
  .tl-row {
411
- animation: tl-row-in var(--duration-slow, 400ms)
411
+ animation: tl-row-in var(--duration-slow, 300ms)
412
412
  var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1)) backwards;
413
413
  animation-delay: calc(var(--tl-i, 0) * var(--duration-stagger, 40ms));
414
414
  transition: background-color var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out);
@@ -65,7 +65,7 @@
65
65
  <style>
66
66
  .time-ring-arc {
67
67
  transition:
68
- stroke-dasharray var(--duration-slow, 400ms)
68
+ stroke-dasharray var(--duration-slow, 300ms)
69
69
  var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1)),
70
70
  color var(--duration-fast, 250ms) var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
71
71
  }
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" generics="T">
2
2
  import type { Snippet } from "svelte";
3
3
  import { cn } from "../../lib/utils.js";
4
+ import { initial } from "../../lib/initials.js";
4
5
  import { TONE_COLOR, TONE_TEXT } from "../../lib/tone.js";
5
6
  import * as Popover from "../../shadcn/popover/index.js";
6
7
  import {
@@ -206,10 +207,6 @@
206
207
  deleting = null;
207
208
  }
208
209
  }
209
-
210
- function initial(name: string): string {
211
- return name.trim().charAt(0).toLocaleUpperCase() || "?";
212
- }
213
210
  </script>
214
211
 
215
212
  <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
@@ -468,7 +465,7 @@
468
465
  }
469
466
 
470
467
  .time-toggle:active {
471
- transform: scale(0.94);
468
+ transform: scale(0.96);
472
469
  }
473
470
 
474
471
  @media (any-pointer: coarse) {
@@ -538,7 +535,7 @@
538
535
  }
539
536
 
540
537
  .time-segment {
541
- transition: width var(--duration-slow, 400ms)
538
+ transition: width var(--duration-slow, 300ms)
542
539
  var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
543
540
  }
544
541
 
@@ -1,21 +0,0 @@
1
- import type { InboxDraft, InboxItem } from "./inbox-types.js";
2
-
3
- /** The draft to open on: the agent's pick, else the first. */
4
- export function startDraft(drafts: InboxDraft[], recommended?: string): InboxDraft | undefined {
5
- return drafts.find((draft) => draft.templateId === recommended) ?? drafts[0];
6
- }
7
-
8
- /** A person can act on an item only while it waits for them. */
9
- export function isOpen(item: InboxItem): boolean {
10
- return item.tier === "ask" && item.state === "proposed";
11
- }
12
-
13
- export function canUndo(item: InboxItem): boolean {
14
- return item.tier === "auto" && item.state === "done";
15
- }
16
-
17
- /** True when a key press belongs to a text field and so isn't a shortcut. */
18
- export function isTyping(target: EventTarget | null): boolean {
19
- if (!(target instanceof HTMLElement)) return false;
20
- return target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
21
- }
@@ -1,51 +0,0 @@
1
- <script lang="ts">
2
- import { Reasoning, ReasoningContent, ReasoningTrigger } from "../../ai/reasoning/index.js";
3
- import { SOURCE_ICON, SOURCE_LABEL } from "./inbox-statuses.js";
4
- import type { InboxTrace } from "./inbox-types.js";
5
-
6
- interface Props {
7
- trace: InboxTrace;
8
- }
9
-
10
- let { trace }: Props = $props();
11
-
12
- let count = $derived(trace.evidence.length);
13
- let SourceIcon = $derived(SOURCE_ICON[trace.source.type]);
14
- </script>
15
-
16
- <!-- WHY the shared Reasoning and not a local disclosure: it is already the
17
- collapsed-by-default "how the agent got here" pattern, with the same chevron. -->
18
- <Reasoning>
19
- <ReasoningTrigger class="w-auto">
20
- <SourceIcon size={14} class="shrink-0" aria-hidden="true" />
21
- <span>
22
- Trace: {SOURCE_LABEL[trace.source.type]}, {trace.source.at}, {count}
23
- {count === 1 ? "quote" : "quotes"}
24
- </span>
25
- </ReasoningTrigger>
26
- <ReasoningContent>
27
- <div class="flex flex-col gap-3">
28
- <p class="text-foreground">
29
- {trace.source.label}
30
- {#if trace.source.href}
31
- <a href={trace.source.href} class="text-foreground ml-1 underline underline-offset-2"
32
- >Open source</a
33
- >
34
- {/if}
35
- </p>
36
- <ul class="flex flex-col gap-2">
37
- {#each trace.evidence as evidence, i (i)}
38
- <li>
39
- <blockquote class="text-foreground">&ldquo;{evidence.quote}&rdquo;</blockquote>
40
- {#if evidence.by || evidence.at}
41
- <span class="text-xs tabular-nums"
42
- >{[evidence.by, evidence.at].filter(Boolean).join(", ")}</span
43
- >
44
- {/if}
45
- </li>
46
- {/each}
47
- </ul>
48
- <p><span class="text-foreground font-medium">Why:</span> {trace.reasoning}</p>
49
- </div>
50
- </ReasoningContent>
51
- </Reasoning>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes