@heyramzi/ui 3.5.0 → 3.7.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 (45) hide show
  1. package/COMPONENTS.md +1 -0
  2. package/dist/ai/chat-window/icons.js +43 -0
  3. package/dist/ai/chat-window/index.js +5 -0
  4. package/dist/ai/chat-window/labels.js +16 -0
  5. package/dist/ai/chat-window/reveal.svelte.js +106 -0
  6. package/dist/ai/chat-window/types.js +4 -0
  7. package/package.json +1 -1
  8. package/src/ai/chat-window/ChatGlyph.svelte +38 -0
  9. package/src/ai/chat-window/ChatGlyph.svelte.d.ts +9 -0
  10. package/src/ai/chat-window/ChatGptWindow.svelte +636 -0
  11. package/src/ai/chat-window/ChatGptWindow.svelte.d.ts +25 -0
  12. package/src/ai/chat-window/ClaudeWindow.svelte +668 -0
  13. package/src/ai/chat-window/ClaudeWindow.svelte.d.ts +25 -0
  14. package/src/ai/chat-window/chat-window.test.ts +80 -0
  15. package/src/ai/chat-window/icons/LICENSE +24 -0
  16. package/src/ai/chat-window/icons/chatgpt-chevron.svg +2 -0
  17. package/src/ai/chat-window/icons/chatgpt-copy.svg +2 -0
  18. package/src/ai/chat-window/icons/chatgpt-mic.svg +2 -0
  19. package/src/ai/chat-window/icons/chatgpt-more.svg +2 -0
  20. package/src/ai/chat-window/icons/chatgpt-plus.svg +2 -0
  21. package/src/ai/chat-window/icons/chatgpt-regenerate.svg +2 -0
  22. package/src/ai/chat-window/icons/chatgpt-share.svg +2 -0
  23. package/src/ai/chat-window/icons/chatgpt-sidebar.svg +1 -0
  24. package/src/ai/chat-window/icons/chatgpt-thumb.svg +2 -0
  25. package/src/ai/chat-window/icons/chatgpt-voice.svg +2 -0
  26. package/src/ai/chat-window/icons/claude-chevron.svg +1 -0
  27. package/src/ai/chat-window/icons/claude-copy.svg +1 -0
  28. package/src/ai/chat-window/icons/claude-plus.svg +1 -0
  29. package/src/ai/chat-window/icons/claude-retry.svg +1 -0
  30. package/src/ai/chat-window/icons/claude-send.svg +1 -0
  31. package/src/ai/chat-window/icons/claude-sidebar.svg +1 -0
  32. package/src/ai/chat-window/icons/claude-sliders.svg +1 -0
  33. package/src/ai/chat-window/icons/claude-spark.svg +1 -0
  34. package/src/ai/chat-window/icons/claude-thumb.svg +1 -0
  35. package/src/ai/chat-window/icons.ts +46 -0
  36. package/src/ai/chat-window/index.ts +26 -0
  37. package/src/ai/chat-window/labels.ts +39 -0
  38. package/src/ai/chat-window/reveal.svelte.ts +106 -0
  39. package/src/ai/chat-window/types.ts +42 -0
  40. package/src/components/open-in/open-in.svelte +14 -15
  41. package/src/components/open-in/open-in.svelte.d.ts +2 -2
  42. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +39 -15
  43. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte.d.ts +2 -1
  44. package/src/shadcn/sidebar/sidebar-provider.svelte +9 -2
  45. package/src/shadcn/sidebar/sidebar-provider.svelte.d.ts +5 -0
package/COMPONENTS.md CHANGED
@@ -125,6 +125,7 @@ Named exports from `@heyramzi/ui/effects/<name>`, e.g.
125
125
  Imported from `@heyramzi/ui/ai/<name>`, e.g. `import { AiButton } from "@heyramzi/ui/ai/ai-button"`.
126
126
 
127
127
  - `ai-button` — AiButton
128
+ - `chat-window` — ChatGptWindow, ClaudeWindow: still or playing pictures of a ChatGPT or Claude conversation for a marketing page, with a connector call row. `messages` takes user text, assistant text or a snippet, and tool steps; every printed string is a prop
128
129
  - `confirmation` — Confirmation, ConfirmationTitle, ConfirmationState (`when` names the kind it shows), ConfirmationActions, ConfirmationAction, type ConfirmationKind
129
130
  - `generation-progress` — GenerationProgress
130
131
  - `loading-state` — PixelLoader
@@ -0,0 +1,43 @@
1
+ // Each glyph is a file a CSS mask paints in the current colour. Sources and licences: icons/LICENSE.
2
+ import gptChevron from "../../../src/ai/chat-window/icons/chatgpt-chevron.svg";
3
+ import gptCopy from "../../../src/ai/chat-window/icons/chatgpt-copy.svg";
4
+ import gptMic from "../../../src/ai/chat-window/icons/chatgpt-mic.svg";
5
+ import gptMore from "../../../src/ai/chat-window/icons/chatgpt-more.svg";
6
+ import gptPlus from "../../../src/ai/chat-window/icons/chatgpt-plus.svg";
7
+ import gptRegenerate from "../../../src/ai/chat-window/icons/chatgpt-regenerate.svg";
8
+ import gptShare from "../../../src/ai/chat-window/icons/chatgpt-share.svg";
9
+ import gptSidebar from "../../../src/ai/chat-window/icons/chatgpt-sidebar.svg";
10
+ import gptThumb from "../../../src/ai/chat-window/icons/chatgpt-thumb.svg";
11
+ import gptVoice from "../../../src/ai/chat-window/icons/chatgpt-voice.svg";
12
+ import claudeChevron from "../../../src/ai/chat-window/icons/claude-chevron.svg";
13
+ import claudeCopy from "../../../src/ai/chat-window/icons/claude-copy.svg";
14
+ import claudePlus from "../../../src/ai/chat-window/icons/claude-plus.svg";
15
+ import claudeRetry from "../../../src/ai/chat-window/icons/claude-retry.svg";
16
+ import claudeSend from "../../../src/ai/chat-window/icons/claude-send.svg";
17
+ import claudeSidebar from "../../../src/ai/chat-window/icons/claude-sidebar.svg";
18
+ import claudeSliders from "../../../src/ai/chat-window/icons/claude-sliders.svg";
19
+ import claudeSpark from "../../../src/ai/chat-window/icons/claude-spark.svg";
20
+ import claudeThumb from "../../../src/ai/chat-window/icons/claude-thumb.svg";
21
+ export const CHATGPT_ICONS = {
22
+ chevron: gptChevron,
23
+ copy: gptCopy,
24
+ mic: gptMic,
25
+ more: gptMore,
26
+ plus: gptPlus,
27
+ regenerate: gptRegenerate,
28
+ share: gptShare,
29
+ sidebar: gptSidebar,
30
+ thumb: gptThumb,
31
+ voice: gptVoice
32
+ };
33
+ export const CLAUDE_ICONS = {
34
+ chevron: claudeChevron,
35
+ copy: claudeCopy,
36
+ plus: claudePlus,
37
+ retry: claudeRetry,
38
+ send: claudeSend,
39
+ sidebar: claudeSidebar,
40
+ sliders: claudeSliders,
41
+ spark: claudeSpark,
42
+ thumb: claudeThumb
43
+ };
@@ -0,0 +1,5 @@
1
+ import ChatGptWindow from "../../../src/ai/chat-window/ChatGptWindow.svelte";
2
+ import ClaudeWindow from "../../../src/ai/chat-window/ClaudeWindow.svelte";
3
+ export { CHATGPT_LABELS, CLAUDE_LABELS } from "./labels.js";
4
+ export { REVEAL_BEATS, revealSchedule } from "./reveal.svelte.js";
5
+ export { ChatGptWindow, ClaudeWindow };
@@ -0,0 +1,16 @@
1
+ export const CHATGPT_LABELS = {
2
+ running: "Calling {name}",
3
+ done: "Used {name}",
4
+ share: "Share",
5
+ disclaimer: "ChatGPT can make mistakes. Check important info.",
6
+ userSaid: "You said:",
7
+ assistantSaid: "ChatGPT said:"
8
+ };
9
+ export const CLAUDE_LABELS = {
10
+ running: "Using {name}",
11
+ done: "Used {name}",
12
+ share: "Share",
13
+ disclaimer: "Claude can make mistakes. Please double-check responses.",
14
+ userSaid: "You said:",
15
+ assistantSaid: "Claude said:"
16
+ };
@@ -0,0 +1,106 @@
1
+ import { untrack } from "svelte";
2
+ /** Milliseconds each beat holds, read as one conversation's pacing, not a transition. */
3
+ export const REVEAL_BEATS = {
4
+ lead: 400,
5
+ user: 900,
6
+ thinking: 900,
7
+ stream: 1500,
8
+ running: 1300,
9
+ after: 350
10
+ };
11
+ /** The timeline for a conversation: when each message enters and settles, and when it ends. */
12
+ export function revealSchedule(roles, beats = REVEAL_BEATS) {
13
+ const out = [];
14
+ let t = beats.lead;
15
+ roles.forEach((role, index) => {
16
+ if (role === "user") {
17
+ out.push({
18
+ at: t,
19
+ index,
20
+ phase: "done"
21
+ });
22
+ t += beats.user;
23
+ } else if (role === "assistant") {
24
+ out.push({
25
+ at: t,
26
+ index,
27
+ phase: "thinking"
28
+ });
29
+ t += beats.thinking;
30
+ out.push({
31
+ at: t,
32
+ index,
33
+ phase: "streaming"
34
+ });
35
+ t += beats.stream;
36
+ out.push({
37
+ at: t,
38
+ index,
39
+ phase: "done"
40
+ });
41
+ t += beats.after;
42
+ } else {
43
+ out.push({
44
+ at: t,
45
+ index,
46
+ phase: "running"
47
+ });
48
+ t += beats.running;
49
+ out.push({
50
+ at: t,
51
+ index,
52
+ phase: "done"
53
+ });
54
+ t += beats.after;
55
+ }
56
+ });
57
+ return {
58
+ beats: out,
59
+ end: t
60
+ };
61
+ }
62
+ /**
63
+ * Plays a conversation once the window is on screen and `active`, again each time `active` turns on.
64
+ * Renders finished for SSR, no JavaScript and reduced motion.
65
+ */
66
+ export function createReveal(options) {
67
+ let phases = $state([]);
68
+ let node = $state(null);
69
+ let seen = $state(false);
70
+ $effect(() => {
71
+ const { enabled } = options();
72
+ if (!node || !enabled || seen) return;
73
+ const io = new IntersectionObserver((entries) => {
74
+ if (entries.some((e) => e.isIntersecting)) {
75
+ seen = true;
76
+ io.disconnect();
77
+ }
78
+ }, { threshold: .35 });
79
+ io.observe(node);
80
+ return () => io.disconnect();
81
+ });
82
+ $effect(() => {
83
+ const { enabled, active } = options();
84
+ if (!enabled || !active || !seen) return;
85
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
86
+ const { roles, hold, ondone } = untrack(options);
87
+ const { beats, end } = revealSchedule(roles);
88
+ phases = roles.map(() => "hidden");
89
+ const timers = beats.map((b) => setTimeout(() => phases[b.index] = b.phase, b.at));
90
+ timers.push(setTimeout(() => ondone?.(), end + hold));
91
+ return () => {
92
+ timers.forEach(clearTimeout);
93
+ phases = [];
94
+ };
95
+ });
96
+ return {
97
+ /** A message's phase; `done` when nothing is playing. */
98
+ phase: (index) => phases[index] ?? "done",
99
+ /** Attach to the window's root, so the reveal waits until it scrolls into view. */
100
+ attach: (el) => {
101
+ node = el;
102
+ return () => node = null;
103
+ },
104
+ streamMs: REVEAL_BEATS.stream
105
+ };
106
+ }
@@ -0,0 +1,4 @@
1
+ /** Fills `{name}` in a label such as "Used {name}". */
2
+ export function fillName(template, name) {
3
+ return template.replaceAll("{name}", name);
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyramzi/ui",
3
- "version": "3.5.0",
3
+ "version": "3.7.0",
4
4
  "description": "Svelte 5 UI for apps: primitives, effects and animations, plus work (tasks and docs), charts, slides, an editor, onboarding and a canvas as subpaths.",
5
5
  "homepage": "https://ui.heyramzi.com",
6
6
  "license": "MIT",
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // PROPS
4
+ // =========================
5
+ interface Props {
6
+ /** The SVG file's URL, painted through a mask in the current colour. */
7
+ src: string;
8
+ size?: number;
9
+ class?: string;
10
+ }
11
+
12
+ let { src, size = 20, class: className = "" }: Props = $props();
13
+ </script>
14
+
15
+ <!-- =========================== -->
16
+ <!-- MARKUP -->
17
+ <!-- =========================== -->
18
+ <span
19
+ class="chat-glyph {className}"
20
+ style:--glyph={`url("${src}")`}
21
+ style:--size="{size}px"
22
+ aria-hidden="true"
23
+ ></span>
24
+
25
+ <style>
26
+ @layer theme, base, components, utilities;
27
+
28
+ @layer components {
29
+ .chat-glyph {
30
+ display: block;
31
+ flex: none;
32
+ width: var(--size);
33
+ height: var(--size);
34
+ background: currentColor;
35
+ mask: var(--glyph) center / contain no-repeat;
36
+ }
37
+ }
38
+ </style>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ /** The SVG file's URL, painted through a mask in the current colour. */
3
+ src: string;
4
+ size?: number;
5
+ class?: string;
6
+ }
7
+ declare const $$Component: import("svelte").Component<Props, {}, "">;
8
+ type $$Component = ReturnType<typeof $$Component>;
9
+ export default $$Component;