@heyramzi/ui 3.4.0 → 3.6.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 (305) hide show
  1. package/COMPONENTS.md +2 -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/dist/components/copy/clipboard.js +3 -3
  8. package/dist/components/flag/codes.js +13 -0
  9. package/dist/components/flag/index.js +2 -0
  10. package/dist/slides/export/pdf.js +7 -0
  11. package/dist/vite/flags.js +46 -0
  12. package/package.json +7 -2
  13. package/src/ai/chat-window/ChatGlyph.svelte +38 -0
  14. package/src/ai/chat-window/ChatGlyph.svelte.d.ts +9 -0
  15. package/src/ai/chat-window/ChatGptWindow.svelte +636 -0
  16. package/src/ai/chat-window/ChatGptWindow.svelte.d.ts +25 -0
  17. package/src/ai/chat-window/ClaudeWindow.svelte +668 -0
  18. package/src/ai/chat-window/ClaudeWindow.svelte.d.ts +25 -0
  19. package/src/ai/chat-window/chat-window.test.ts +80 -0
  20. package/src/ai/chat-window/icons/LICENSE +24 -0
  21. package/src/ai/chat-window/icons/chatgpt-chevron.svg +2 -0
  22. package/src/ai/chat-window/icons/chatgpt-copy.svg +2 -0
  23. package/src/ai/chat-window/icons/chatgpt-mic.svg +2 -0
  24. package/src/ai/chat-window/icons/chatgpt-more.svg +2 -0
  25. package/src/ai/chat-window/icons/chatgpt-plus.svg +2 -0
  26. package/src/ai/chat-window/icons/chatgpt-regenerate.svg +2 -0
  27. package/src/ai/chat-window/icons/chatgpt-share.svg +2 -0
  28. package/src/ai/chat-window/icons/chatgpt-sidebar.svg +1 -0
  29. package/src/ai/chat-window/icons/chatgpt-thumb.svg +2 -0
  30. package/src/ai/chat-window/icons/chatgpt-voice.svg +2 -0
  31. package/src/ai/chat-window/icons/claude-chevron.svg +1 -0
  32. package/src/ai/chat-window/icons/claude-copy.svg +1 -0
  33. package/src/ai/chat-window/icons/claude-plus.svg +1 -0
  34. package/src/ai/chat-window/icons/claude-retry.svg +1 -0
  35. package/src/ai/chat-window/icons/claude-send.svg +1 -0
  36. package/src/ai/chat-window/icons/claude-sidebar.svg +1 -0
  37. package/src/ai/chat-window/icons/claude-sliders.svg +1 -0
  38. package/src/ai/chat-window/icons/claude-spark.svg +1 -0
  39. package/src/ai/chat-window/icons/claude-thumb.svg +1 -0
  40. package/src/ai/chat-window/icons.ts +46 -0
  41. package/src/ai/chat-window/index.ts +26 -0
  42. package/src/ai/chat-window/labels.ts +39 -0
  43. package/src/ai/chat-window/reveal.svelte.ts +106 -0
  44. package/src/ai/chat-window/types.ts +42 -0
  45. package/src/components/copy/clipboard.test.ts +17 -1
  46. package/src/components/copy/clipboard.ts +3 -3
  47. package/src/components/flag/codes.test.ts +30 -0
  48. package/src/components/flag/codes.ts +25 -0
  49. package/src/components/flag/flag.svelte +51 -0
  50. package/src/components/flag/flag.svelte.d.ts +12 -0
  51. package/src/components/flag/flags/AD.svg +1 -0
  52. package/src/components/flag/flags/AE.svg +1 -0
  53. package/src/components/flag/flags/AF.svg +1 -0
  54. package/src/components/flag/flags/AG.svg +1 -0
  55. package/src/components/flag/flags/AI.svg +1 -0
  56. package/src/components/flag/flags/AL.svg +1 -0
  57. package/src/components/flag/flags/AM.svg +1 -0
  58. package/src/components/flag/flags/AO.svg +1 -0
  59. package/src/components/flag/flags/AQ.svg +1 -0
  60. package/src/components/flag/flags/AR.svg +1 -0
  61. package/src/components/flag/flags/AS.svg +1 -0
  62. package/src/components/flag/flags/AT.svg +1 -0
  63. package/src/components/flag/flags/AU.svg +1 -0
  64. package/src/components/flag/flags/AW.svg +1 -0
  65. package/src/components/flag/flags/AX.svg +1 -0
  66. package/src/components/flag/flags/AZ.svg +1 -0
  67. package/src/components/flag/flags/BA.svg +1 -0
  68. package/src/components/flag/flags/BB.svg +1 -0
  69. package/src/components/flag/flags/BD.svg +1 -0
  70. package/src/components/flag/flags/BE.svg +1 -0
  71. package/src/components/flag/flags/BF.svg +1 -0
  72. package/src/components/flag/flags/BG.svg +1 -0
  73. package/src/components/flag/flags/BH.svg +1 -0
  74. package/src/components/flag/flags/BI.svg +1 -0
  75. package/src/components/flag/flags/BJ.svg +1 -0
  76. package/src/components/flag/flags/BL.svg +1 -0
  77. package/src/components/flag/flags/BM.svg +1 -0
  78. package/src/components/flag/flags/BN.svg +1 -0
  79. package/src/components/flag/flags/BO.svg +1 -0
  80. package/src/components/flag/flags/BQ.svg +1 -0
  81. package/src/components/flag/flags/BR.svg +1 -0
  82. package/src/components/flag/flags/BS.svg +1 -0
  83. package/src/components/flag/flags/BT.svg +1 -0
  84. package/src/components/flag/flags/BV.svg +1 -0
  85. package/src/components/flag/flags/BW.svg +1 -0
  86. package/src/components/flag/flags/BY.svg +1 -0
  87. package/src/components/flag/flags/BZ.svg +1 -0
  88. package/src/components/flag/flags/CA.svg +1 -0
  89. package/src/components/flag/flags/CC.svg +1 -0
  90. package/src/components/flag/flags/CD.svg +1 -0
  91. package/src/components/flag/flags/CF.svg +1 -0
  92. package/src/components/flag/flags/CG.svg +1 -0
  93. package/src/components/flag/flags/CH.svg +1 -0
  94. package/src/components/flag/flags/CI.svg +1 -0
  95. package/src/components/flag/flags/CK.svg +1 -0
  96. package/src/components/flag/flags/CL.svg +1 -0
  97. package/src/components/flag/flags/CM.svg +1 -0
  98. package/src/components/flag/flags/CN.svg +1 -0
  99. package/src/components/flag/flags/CO.svg +1 -0
  100. package/src/components/flag/flags/CR.svg +1 -0
  101. package/src/components/flag/flags/CU.svg +1 -0
  102. package/src/components/flag/flags/CV.svg +1 -0
  103. package/src/components/flag/flags/CW.svg +1 -0
  104. package/src/components/flag/flags/CX.svg +1 -0
  105. package/src/components/flag/flags/CY.svg +1 -0
  106. package/src/components/flag/flags/CZ.svg +1 -0
  107. package/src/components/flag/flags/DE.svg +1 -0
  108. package/src/components/flag/flags/DJ.svg +1 -0
  109. package/src/components/flag/flags/DK.svg +1 -0
  110. package/src/components/flag/flags/DM.svg +1 -0
  111. package/src/components/flag/flags/DO.svg +1 -0
  112. package/src/components/flag/flags/DZ.svg +1 -0
  113. package/src/components/flag/flags/EC.svg +1 -0
  114. package/src/components/flag/flags/EE.svg +1 -0
  115. package/src/components/flag/flags/EG.svg +1 -0
  116. package/src/components/flag/flags/EH.svg +1 -0
  117. package/src/components/flag/flags/ER.svg +1 -0
  118. package/src/components/flag/flags/ES.svg +1 -0
  119. package/src/components/flag/flags/ET.svg +1 -0
  120. package/src/components/flag/flags/FI.svg +1 -0
  121. package/src/components/flag/flags/FJ.svg +1 -0
  122. package/src/components/flag/flags/FK.svg +1 -0
  123. package/src/components/flag/flags/FM.svg +1 -0
  124. package/src/components/flag/flags/FO.svg +1 -0
  125. package/src/components/flag/flags/FR.svg +1 -0
  126. package/src/components/flag/flags/GA.svg +1 -0
  127. package/src/components/flag/flags/GB.svg +1 -0
  128. package/src/components/flag/flags/GD.svg +1 -0
  129. package/src/components/flag/flags/GE.svg +1 -0
  130. package/src/components/flag/flags/GF.svg +1 -0
  131. package/src/components/flag/flags/GG.svg +1 -0
  132. package/src/components/flag/flags/GH.svg +1 -0
  133. package/src/components/flag/flags/GI.svg +1 -0
  134. package/src/components/flag/flags/GL.svg +1 -0
  135. package/src/components/flag/flags/GM.svg +1 -0
  136. package/src/components/flag/flags/GN.svg +1 -0
  137. package/src/components/flag/flags/GP.svg +1 -0
  138. package/src/components/flag/flags/GQ.svg +1 -0
  139. package/src/components/flag/flags/GR.svg +1 -0
  140. package/src/components/flag/flags/GS.svg +1 -0
  141. package/src/components/flag/flags/GT.svg +1 -0
  142. package/src/components/flag/flags/GU.svg +1 -0
  143. package/src/components/flag/flags/GW.svg +1 -0
  144. package/src/components/flag/flags/GY.svg +1 -0
  145. package/src/components/flag/flags/HK.svg +1 -0
  146. package/src/components/flag/flags/HM.svg +1 -0
  147. package/src/components/flag/flags/HN.svg +1 -0
  148. package/src/components/flag/flags/HR.svg +1 -0
  149. package/src/components/flag/flags/HT.svg +1 -0
  150. package/src/components/flag/flags/HU.svg +1 -0
  151. package/src/components/flag/flags/ID.svg +1 -0
  152. package/src/components/flag/flags/IE.svg +1 -0
  153. package/src/components/flag/flags/IL.svg +1 -0
  154. package/src/components/flag/flags/IM.svg +1 -0
  155. package/src/components/flag/flags/IN.svg +1 -0
  156. package/src/components/flag/flags/IO.svg +1 -0
  157. package/src/components/flag/flags/IQ.svg +1 -0
  158. package/src/components/flag/flags/IR.svg +1 -0
  159. package/src/components/flag/flags/IS.svg +1 -0
  160. package/src/components/flag/flags/IT.svg +1 -0
  161. package/src/components/flag/flags/JE.svg +1 -0
  162. package/src/components/flag/flags/JM.svg +1 -0
  163. package/src/components/flag/flags/JO.svg +1 -0
  164. package/src/components/flag/flags/JP.svg +1 -0
  165. package/src/components/flag/flags/KE.svg +1 -0
  166. package/src/components/flag/flags/KG.svg +1 -0
  167. package/src/components/flag/flags/KH.svg +1 -0
  168. package/src/components/flag/flags/KI.svg +1 -0
  169. package/src/components/flag/flags/KM.svg +1 -0
  170. package/src/components/flag/flags/KN.svg +1 -0
  171. package/src/components/flag/flags/KP.svg +1 -0
  172. package/src/components/flag/flags/KR.svg +1 -0
  173. package/src/components/flag/flags/KW.svg +1 -0
  174. package/src/components/flag/flags/KY.svg +1 -0
  175. package/src/components/flag/flags/KZ.svg +1 -0
  176. package/src/components/flag/flags/LA.svg +1 -0
  177. package/src/components/flag/flags/LB.svg +1 -0
  178. package/src/components/flag/flags/LC.svg +1 -0
  179. package/src/components/flag/flags/LI.svg +1 -0
  180. package/src/components/flag/flags/LICENSE +22 -0
  181. package/src/components/flag/flags/LK.svg +1 -0
  182. package/src/components/flag/flags/LR.svg +1 -0
  183. package/src/components/flag/flags/LS.svg +1 -0
  184. package/src/components/flag/flags/LT.svg +1 -0
  185. package/src/components/flag/flags/LU.svg +1 -0
  186. package/src/components/flag/flags/LV.svg +1 -0
  187. package/src/components/flag/flags/LY.svg +1 -0
  188. package/src/components/flag/flags/MA.svg +1 -0
  189. package/src/components/flag/flags/MC.svg +1 -0
  190. package/src/components/flag/flags/MD.svg +1 -0
  191. package/src/components/flag/flags/ME.svg +1 -0
  192. package/src/components/flag/flags/MF.svg +1 -0
  193. package/src/components/flag/flags/MG.svg +1 -0
  194. package/src/components/flag/flags/MH.svg +1 -0
  195. package/src/components/flag/flags/MK.svg +1 -0
  196. package/src/components/flag/flags/ML.svg +1 -0
  197. package/src/components/flag/flags/MM.svg +1 -0
  198. package/src/components/flag/flags/MN.svg +1 -0
  199. package/src/components/flag/flags/MO.svg +1 -0
  200. package/src/components/flag/flags/MP.svg +1 -0
  201. package/src/components/flag/flags/MQ.svg +1 -0
  202. package/src/components/flag/flags/MR.svg +1 -0
  203. package/src/components/flag/flags/MS.svg +1 -0
  204. package/src/components/flag/flags/MT.svg +1 -0
  205. package/src/components/flag/flags/MU.svg +1 -0
  206. package/src/components/flag/flags/MV.svg +1 -0
  207. package/src/components/flag/flags/MW.svg +1 -0
  208. package/src/components/flag/flags/MX.svg +1 -0
  209. package/src/components/flag/flags/MY.svg +1 -0
  210. package/src/components/flag/flags/MZ.svg +1 -0
  211. package/src/components/flag/flags/NA.svg +1 -0
  212. package/src/components/flag/flags/NC.svg +1 -0
  213. package/src/components/flag/flags/NE.svg +1 -0
  214. package/src/components/flag/flags/NF.svg +1 -0
  215. package/src/components/flag/flags/NG.svg +1 -0
  216. package/src/components/flag/flags/NI.svg +1 -0
  217. package/src/components/flag/flags/NL.svg +1 -0
  218. package/src/components/flag/flags/NO.svg +1 -0
  219. package/src/components/flag/flags/NP.svg +1 -0
  220. package/src/components/flag/flags/NR.svg +1 -0
  221. package/src/components/flag/flags/NU.svg +1 -0
  222. package/src/components/flag/flags/NZ.svg +1 -0
  223. package/src/components/flag/flags/OM.svg +1 -0
  224. package/src/components/flag/flags/PA.svg +1 -0
  225. package/src/components/flag/flags/PE.svg +1 -0
  226. package/src/components/flag/flags/PF.svg +1 -0
  227. package/src/components/flag/flags/PG.svg +1 -0
  228. package/src/components/flag/flags/PH.svg +1 -0
  229. package/src/components/flag/flags/PK.svg +1 -0
  230. package/src/components/flag/flags/PL.svg +1 -0
  231. package/src/components/flag/flags/PM.svg +1 -0
  232. package/src/components/flag/flags/PN.svg +1 -0
  233. package/src/components/flag/flags/PR.svg +1 -0
  234. package/src/components/flag/flags/PS.svg +1 -0
  235. package/src/components/flag/flags/PT.svg +1 -0
  236. package/src/components/flag/flags/PW.svg +1 -0
  237. package/src/components/flag/flags/PY.svg +1 -0
  238. package/src/components/flag/flags/QA.svg +1 -0
  239. package/src/components/flag/flags/RE.svg +1 -0
  240. package/src/components/flag/flags/RO.svg +1 -0
  241. package/src/components/flag/flags/RS.svg +1 -0
  242. package/src/components/flag/flags/RU.svg +1 -0
  243. package/src/components/flag/flags/RW.svg +1 -0
  244. package/src/components/flag/flags/SA.svg +1 -0
  245. package/src/components/flag/flags/SB.svg +1 -0
  246. package/src/components/flag/flags/SC.svg +1 -0
  247. package/src/components/flag/flags/SD.svg +1 -0
  248. package/src/components/flag/flags/SE.svg +1 -0
  249. package/src/components/flag/flags/SG.svg +1 -0
  250. package/src/components/flag/flags/SH.svg +1 -0
  251. package/src/components/flag/flags/SI.svg +1 -0
  252. package/src/components/flag/flags/SJ.svg +1 -0
  253. package/src/components/flag/flags/SK.svg +1 -0
  254. package/src/components/flag/flags/SL.svg +1 -0
  255. package/src/components/flag/flags/SM.svg +1 -0
  256. package/src/components/flag/flags/SN.svg +1 -0
  257. package/src/components/flag/flags/SO.svg +1 -0
  258. package/src/components/flag/flags/SR.svg +1 -0
  259. package/src/components/flag/flags/SS.svg +1 -0
  260. package/src/components/flag/flags/ST.svg +1 -0
  261. package/src/components/flag/flags/SV.svg +1 -0
  262. package/src/components/flag/flags/SX.svg +1 -0
  263. package/src/components/flag/flags/SY.svg +1 -0
  264. package/src/components/flag/flags/SZ.svg +1 -0
  265. package/src/components/flag/flags/TC.svg +1 -0
  266. package/src/components/flag/flags/TD.svg +1 -0
  267. package/src/components/flag/flags/TF.svg +1 -0
  268. package/src/components/flag/flags/TG.svg +1 -0
  269. package/src/components/flag/flags/TH.svg +1 -0
  270. package/src/components/flag/flags/TJ.svg +1 -0
  271. package/src/components/flag/flags/TK.svg +1 -0
  272. package/src/components/flag/flags/TL.svg +1 -0
  273. package/src/components/flag/flags/TM.svg +1 -0
  274. package/src/components/flag/flags/TN.svg +1 -0
  275. package/src/components/flag/flags/TO.svg +1 -0
  276. package/src/components/flag/flags/TR.svg +1 -0
  277. package/src/components/flag/flags/TT.svg +1 -0
  278. package/src/components/flag/flags/TV.svg +1 -0
  279. package/src/components/flag/flags/TW.svg +1 -0
  280. package/src/components/flag/flags/TZ.svg +1 -0
  281. package/src/components/flag/flags/UA.svg +1 -0
  282. package/src/components/flag/flags/UG.svg +1 -0
  283. package/src/components/flag/flags/UM.svg +1 -0
  284. package/src/components/flag/flags/US.svg +1 -0
  285. package/src/components/flag/flags/UY.svg +1 -0
  286. package/src/components/flag/flags/UZ.svg +1 -0
  287. package/src/components/flag/flags/VA.svg +1 -0
  288. package/src/components/flag/flags/VC.svg +1 -0
  289. package/src/components/flag/flags/VE.svg +1 -0
  290. package/src/components/flag/flags/VG.svg +1 -0
  291. package/src/components/flag/flags/VI.svg +1 -0
  292. package/src/components/flag/flags/VN.svg +1 -0
  293. package/src/components/flag/flags/VU.svg +1 -0
  294. package/src/components/flag/flags/WF.svg +1 -0
  295. package/src/components/flag/flags/WS.svg +1 -0
  296. package/src/components/flag/flags/XK.svg +1 -0
  297. package/src/components/flag/flags/YE.svg +1 -0
  298. package/src/components/flag/flags/YT.svg +1 -0
  299. package/src/components/flag/flags/ZA.svg +1 -0
  300. package/src/components/flag/flags/ZM.svg +1 -0
  301. package/src/components/flag/flags/ZW.svg +1 -0
  302. package/src/components/flag/index.ts +2 -0
  303. package/src/slides/export/pdf.ts +11 -0
  304. package/src/vite/flags.test.ts +38 -0
  305. package/src/vite/flags.ts +63 -0
@@ -0,0 +1,636 @@
1
+ <script lang="ts" module>
2
+ import type { ChatGptLabels } from "./labels.js";
3
+ import type { ChatMessage, ChatTheme } from "./types.js";
4
+
5
+ export interface ChatGptWindowProps {
6
+ /** The conversation's name, read out as the picture's label. */
7
+ title: string;
8
+ messages: ChatMessage[];
9
+ /** The picker in the bar. */
10
+ model?: string;
11
+ placeholder?: string;
12
+ theme?: ChatTheme;
13
+ /** Plays the messages in once the window is on screen. Off, it is a still. */
14
+ reveal?: boolean;
15
+ /** With `reveal`, plays each time this turns on, for a rotator. */
16
+ active?: boolean;
17
+ /** Called after the reveal ends and `hold` ms have passed. */
18
+ ondone?: () => void;
19
+ hold?: number;
20
+ /** Draws macOS's traffic lights, as the desktop app. */
21
+ frame?: boolean;
22
+ labels?: Partial<ChatGptLabels>;
23
+ class?: string;
24
+ }
25
+ </script>
26
+
27
+ <script lang="ts">
28
+ // =========================
29
+ // IMPORTS
30
+ // =========================
31
+ import ChatGlyph from "./ChatGlyph.svelte";
32
+ import { CHATGPT_ICONS as I } from "./icons.js";
33
+ import { CHATGPT_LABELS } from "./labels.js";
34
+ import { createReveal } from "./reveal.svelte.js";
35
+ import { fillName } from "./types.js";
36
+
37
+ // =========================
38
+ // PROPS
39
+ // =========================
40
+ let {
41
+ title,
42
+ messages,
43
+ model = "ChatGPT",
44
+ placeholder = "Ask anything",
45
+ theme = "auto",
46
+ reveal = false,
47
+ active = true,
48
+ ondone,
49
+ hold = 4000,
50
+ frame = true,
51
+ labels = {},
52
+ class: className = "",
53
+ }: ChatGptWindowProps = $props();
54
+
55
+ // =========================
56
+ // STATE
57
+ // =========================
58
+ const text = $derived({ ...CHATGPT_LABELS, ...labels });
59
+ const lastAssistant = $derived(messages.findLastIndex((m) => m.role === "assistant"));
60
+ const player = createReveal(() => ({
61
+ roles: messages.map((m) => m.role),
62
+ enabled: reveal,
63
+ active,
64
+ hold,
65
+ ondone,
66
+ }));
67
+ </script>
68
+
69
+ <!-- =========================== -->
70
+ <!-- MARKUP -->
71
+ <!-- =========================== -->
72
+ <figure
73
+ class="gpt {className}"
74
+ data-mode={theme}
75
+ aria-label="ChatGPT: {title}"
76
+ style:--gpt-stream="{player.streamMs}ms"
77
+ {@attach player.attach}
78
+ >
79
+ <div class="bar" aria-hidden="true">
80
+ {#if frame}
81
+ <span class="lights"><i></i><i></i><i></i></span>
82
+ {/if}
83
+ <span class="ib"><ChatGlyph src={I.sidebar} /></span>
84
+ <span class="model"
85
+ >{model}<span class="dim"><ChatGlyph src={I.chevron} size={16} /></span></span
86
+ >
87
+ <span class="share wide"><ChatGlyph src={I.share} />{text.share}</span>
88
+ <span class="ib end"><ChatGlyph src={I.more} /></span>
89
+ </div>
90
+
91
+ <div class="scroll">
92
+ <ol class="thread">
93
+ {#each messages as message, i (i)}
94
+ {@const phase = player.phase(i)}
95
+ <li class="turn {message.role}" data-phase={phase}>
96
+ {#if message.role === "user"}
97
+ <span class="vh">{text.userSaid}</span>
98
+ <p class="bubble">{message.text}</p>
99
+ {:else if message.role === "assistant"}
100
+ <span class="think" aria-hidden="true"></span>
101
+ <div class="reply">
102
+ <span class="vh">{text.assistantSaid}</span>
103
+ {#if message.content}
104
+ {@render message.content()}
105
+ {:else if message.text}
106
+ <p>{message.text}</p>
107
+ {/if}
108
+ </div>
109
+ {#if i === lastAssistant}
110
+ <div class="actions" aria-hidden="true">
111
+ <span class="ab"><ChatGlyph src={I.copy} /></span>
112
+ <span class="ab"><ChatGlyph src={I.thumb} /></span>
113
+ <span class="ab flip"><ChatGlyph src={I.thumb} /></span>
114
+ <span class="ab"><ChatGlyph src={I.share} /></span>
115
+ <span class="ab wide"><ChatGlyph src={I.regenerate} /></span>
116
+ <span class="ab"><ChatGlyph src={I.more} /></span>
117
+ </div>
118
+ {/if}
119
+ {:else}
120
+ <!-- An app call, as ChatGPT draws one between two parts of a reply: its label shimmers while it runs. -->
121
+ <p class="call">
122
+ <span class="tool-icon" aria-hidden="true">
123
+ {#if message.icon}
124
+ {@render message.icon()}
125
+ {:else if message.iconSrc}
126
+ <img src={message.iconSrc} alt="" width="20" height="20" />
127
+ {:else}
128
+ <span class="tool-letter">{message.name.slice(0, 1)}</span>
129
+ {/if}
130
+ </span>
131
+ <span class="tool-text">
132
+ {fillName(phase === "running" ? text.running : text.done, message.name)}
133
+ </span>
134
+ {#if message.label}
135
+ <span class="tool-label">{message.label}</span>
136
+ {/if}
137
+ <span class="tool-chev" aria-hidden="true"
138
+ ><ChatGlyph src={I.chevron} size={16} /></span
139
+ >
140
+ </p>
141
+ {/if}
142
+ </li>
143
+ {/each}
144
+ </ol>
145
+ </div>
146
+
147
+ <div class="dock" aria-hidden="true">
148
+ <div class="composer">
149
+ <span class="ib round"><ChatGlyph src={I.plus} size={22} /></span>
150
+ <span class="prompt">{placeholder}</span>
151
+ <span class="ib round wide"><ChatGlyph src={I.mic} /></span>
152
+ <span class="voice"><ChatGlyph src={I.voice} /></span>
153
+ </div>
154
+ <span class="note">{text.disclaimer}</span>
155
+ </div>
156
+ </figure>
157
+
158
+ <style>
159
+ @layer theme, base, components, utilities;
160
+
161
+ @layer components {
162
+ /* chatgpt.com's signed-in tokens, measured 11 Nov 2026. A connector needs an account, so this is the screen its users see. */
163
+ .gpt {
164
+ --gpt-bg: light-dark(#ffffff, #212121);
165
+ --gpt-bar: light-dark(#ffffff, #212121);
166
+ --gpt-bubble: light-dark(#f4f4f4, #303030);
167
+ --gpt-ink: light-dark(#0d0d0d, #ffffff);
168
+ --gpt-soft: light-dark(#5d5d5d, #afafaf);
169
+ --gpt-faint: light-dark(#767676, #979797);
170
+ --gpt-line: light-dark(rgb(13 13 13 / 0.07), rgb(255 255 255 / 0.1));
171
+ --gpt-rim: light-dark(rgb(0 0 0 / 0.1), rgb(255 255 255 / 0.12));
172
+ --gpt-composer: light-dark(#ffffff, #303030);
173
+ --gpt-solid: light-dark(#000000, #ffffff);
174
+ --gpt-on-solid: light-dark(#ffffff, #000000);
175
+ --gpt-letter: light-dark(#e8e8e8, #424242);
176
+
177
+ all: initial;
178
+ box-sizing: border-box;
179
+ display: grid;
180
+ grid-template-rows: auto minmax(0, 1fr) auto;
181
+ grid-template-columns: minmax(0, 1fr);
182
+ width: 100%;
183
+ height: 100%;
184
+ margin: 0;
185
+ container-type: inline-size;
186
+ overflow: hidden;
187
+ color-scheme: inherit;
188
+ /* The Mac window's 12px corner, not a kit role: it is the product's own. */
189
+ border-radius: 12px;
190
+ background: var(--gpt-bg);
191
+ color: var(--gpt-ink);
192
+ box-shadow:
193
+ 0 0 0 1px var(--gpt-rim),
194
+ var(--elev-overlay);
195
+ font-family:
196
+ ui-sans-serif,
197
+ -apple-system,
198
+ system-ui,
199
+ "Segoe UI",
200
+ Helvetica,
201
+ Arial,
202
+ sans-serif;
203
+ font-size: 16px;
204
+ line-height: 1.5;
205
+ text-align: left;
206
+ -webkit-font-smoothing: antialiased;
207
+ -moz-osx-font-smoothing: grayscale;
208
+ user-select: none;
209
+ }
210
+
211
+ .gpt[data-mode="light"] {
212
+ color-scheme: light;
213
+ }
214
+
215
+ .gpt[data-mode="dark"],
216
+ :global(.dark) .gpt[data-mode="auto"],
217
+ :global([data-theme="dark"]) .gpt[data-mode="auto"] {
218
+ color-scheme: dark;
219
+ }
220
+
221
+ :global([data-theme="light"]) .gpt[data-mode="auto"] {
222
+ color-scheme: light;
223
+ }
224
+
225
+ .gpt *,
226
+ .gpt *::before,
227
+ .gpt *::after {
228
+ box-sizing: border-box;
229
+ }
230
+
231
+ .vh {
232
+ position: absolute;
233
+ width: 1px;
234
+ height: 1px;
235
+ overflow: hidden;
236
+ clip-path: inset(50%);
237
+ white-space: nowrap;
238
+ }
239
+
240
+ /* A fake icon button: a span, so no site button style reaches it and nothing takes focus. */
241
+ .ib {
242
+ display: grid;
243
+ place-items: center;
244
+ flex: none;
245
+ width: 36px;
246
+ height: 36px;
247
+ border-radius: 10px;
248
+ color: var(--gpt-ink);
249
+ }
250
+
251
+ .round {
252
+ border-radius: 50%;
253
+ }
254
+
255
+ /* ---------- the bar ---------- */
256
+ .bar {
257
+ display: flex;
258
+ align-items: center;
259
+ gap: 2px;
260
+ height: 52px;
261
+ padding: 0 8px 0 14px;
262
+ background: var(--gpt-bar);
263
+ }
264
+
265
+ .lights {
266
+ display: flex;
267
+ gap: 8px;
268
+ margin-right: 12px;
269
+ }
270
+
271
+ .lights i {
272
+ width: 12px;
273
+ height: 12px;
274
+ border-radius: 50%;
275
+ background: #ff5f57;
276
+ box-shadow: inset 0 0 0 0.5px rgb(0 0 0 / 0.18);
277
+ }
278
+
279
+ .lights i:nth-child(2) {
280
+ background: #febc2e;
281
+ }
282
+
283
+ .lights i:nth-child(3) {
284
+ background: #28c840;
285
+ }
286
+
287
+ .model {
288
+ display: inline-flex;
289
+ align-items: center;
290
+ gap: 4px;
291
+ min-width: 0;
292
+ height: 36px;
293
+ padding: 0 8px;
294
+ font-size: 18px;
295
+ margin-right: auto;
296
+ line-height: 1.5556;
297
+ white-space: nowrap;
298
+ }
299
+
300
+ .dim {
301
+ color: var(--gpt-soft);
302
+ }
303
+
304
+ .share {
305
+ display: inline-flex;
306
+ align-items: center;
307
+ gap: 6px;
308
+ height: 36px;
309
+ padding: 0 12px 0 10px;
310
+ border-radius: 999px;
311
+ font-size: 14px;
312
+ font-weight: 500;
313
+ }
314
+
315
+ /* ---------- the conversation ---------- */
316
+ /* End-aligned: a chat sits scrolled to its newest turn, so a short window crops the top, never the composer. */
317
+ .scroll {
318
+ display: flex;
319
+ flex-direction: column;
320
+ justify-content: flex-end;
321
+ min-height: 0;
322
+ overflow: hidden;
323
+ }
324
+
325
+ .thread {
326
+ display: grid;
327
+ gap: 24px;
328
+ width: 100%;
329
+ max-width: 768px;
330
+ margin: 0 auto;
331
+ padding: 12px clamp(16px, 5cqi, 32px) 20px;
332
+ list-style: none;
333
+ }
334
+
335
+ .turn {
336
+ display: grid;
337
+ min-width: 0;
338
+ }
339
+
340
+ .user {
341
+ justify-items: end;
342
+ }
343
+
344
+ .bubble {
345
+ max-width: 70%;
346
+ margin: 0;
347
+ padding: 10px 16px;
348
+ border-radius: 22px;
349
+ background: var(--gpt-bubble);
350
+ overflow-wrap: anywhere;
351
+ white-space: pre-wrap;
352
+ }
353
+
354
+ /* The reply and the thinking dot share one cell, so the dot never pushes the reply down. */
355
+ .assistant > .think,
356
+ .assistant > .reply {
357
+ grid-area: 1 / 1;
358
+ }
359
+
360
+ .think {
361
+ align-self: start;
362
+ width: 12px;
363
+ height: 12px;
364
+ margin: 8px 0 0 2px;
365
+ border-radius: 50%;
366
+ background: var(--gpt-ink);
367
+ opacity: 0;
368
+ }
369
+
370
+ .reply {
371
+ display: grid;
372
+ gap: 16px;
373
+ min-width: 0;
374
+ line-height: 1.75;
375
+ overflow-wrap: anywhere;
376
+ }
377
+
378
+ /* What a site renders in the snippet takes ChatGPT's markdown look. */
379
+ .reply :global(:is(p, ul, ol, h3, table)) {
380
+ margin: 0;
381
+ }
382
+
383
+ .reply :global(:is(ul, ol)) {
384
+ padding-left: 1.5em;
385
+ }
386
+
387
+ .reply :global(:is(strong, b, h3, th)) {
388
+ font-weight: 600;
389
+ }
390
+
391
+ .reply :global(h3) {
392
+ font-size: 18px;
393
+ }
394
+
395
+ .reply :global(table) {
396
+ width: 100%;
397
+ border-collapse: collapse;
398
+ font-size: 14px;
399
+ line-height: 1.5;
400
+ font-variant-numeric: tabular-nums;
401
+ }
402
+
403
+ .reply :global(:is(th, td)) {
404
+ padding: 8px 8px 8px 0;
405
+ border-bottom: 1px solid var(--gpt-line);
406
+ text-align: left;
407
+ }
408
+
409
+ .reply :global(tr:last-child td) {
410
+ border-bottom: 0;
411
+ }
412
+
413
+ .actions {
414
+ display: flex;
415
+ margin: 6px 0 0 -8px;
416
+ }
417
+
418
+ .ab {
419
+ display: grid;
420
+ place-items: center;
421
+ width: 32px;
422
+ height: 32px;
423
+ border-radius: 8px;
424
+ color: var(--gpt-soft);
425
+ }
426
+
427
+ .flip {
428
+ scale: 1 -1;
429
+ }
430
+
431
+ .call {
432
+ display: flex;
433
+ align-items: center;
434
+ gap: 8px;
435
+ min-width: 0;
436
+ margin: 0;
437
+ font-size: 14px;
438
+ line-height: 1.4286;
439
+ color: var(--gpt-soft);
440
+ }
441
+
442
+ .tool-icon {
443
+ display: grid;
444
+ place-items: center;
445
+ flex: none;
446
+ width: 20px;
447
+ height: 20px;
448
+ overflow: hidden;
449
+ border-radius: 6px;
450
+ }
451
+
452
+ .tool-icon img {
453
+ display: block;
454
+ width: 100%;
455
+ height: 100%;
456
+ object-fit: cover;
457
+ }
458
+
459
+ .tool-letter {
460
+ display: grid;
461
+ place-items: center;
462
+ width: 100%;
463
+ height: 100%;
464
+ background: var(--gpt-letter);
465
+ color: var(--gpt-ink);
466
+ font-size: 12px;
467
+ font-weight: 600;
468
+ }
469
+
470
+ .tool-text {
471
+ flex: none;
472
+ font-weight: 500;
473
+ color: var(--gpt-ink);
474
+ }
475
+
476
+ .tool-label {
477
+ min-width: 0;
478
+ overflow: hidden;
479
+ white-space: nowrap;
480
+ text-overflow: ellipsis;
481
+ }
482
+
483
+ .tool-chev {
484
+ flex: none;
485
+ margin-left: -2px;
486
+ rotate: -90deg;
487
+ }
488
+
489
+ /* ---------- the composer ---------- */
490
+ .dock {
491
+ display: grid;
492
+ grid-template-columns: minmax(0, 1fr);
493
+ justify-items: center;
494
+ gap: 8px;
495
+ padding: 0 clamp(12px, 4cqi, 24px) 10px;
496
+ }
497
+
498
+ .composer {
499
+ display: flex;
500
+ align-items: center;
501
+ gap: 4px;
502
+ width: 100%;
503
+ max-width: 768px;
504
+ min-height: 52px;
505
+ padding: 6px 8px 6px 6px;
506
+ border-radius: 28px;
507
+ background: var(--gpt-composer);
508
+ box-shadow:
509
+ 0 0 0 1px light-dark(rgb(0 0 0 / 0.04), transparent),
510
+ 0 2px 8px light-dark(rgb(0 0 0 / 0.04), transparent),
511
+ 0 4px 80px 8px light-dark(rgb(0 0 0 / 0.024), transparent),
512
+ inset 0 0 1px light-dark(transparent, rgb(255 255 255 / 0.2));
513
+ color: var(--gpt-faint);
514
+ }
515
+
516
+ .prompt {
517
+ flex: 1;
518
+ min-width: 0;
519
+ padding-left: 4px;
520
+ overflow: hidden;
521
+ white-space: nowrap;
522
+ text-overflow: ellipsis;
523
+ }
524
+
525
+ .voice {
526
+ display: grid;
527
+ place-items: center;
528
+ flex: none;
529
+ width: 36px;
530
+ height: 36px;
531
+ border-radius: 50%;
532
+ background: var(--gpt-solid);
533
+ color: var(--gpt-on-solid);
534
+ }
535
+
536
+ .note {
537
+ font-size: 12px;
538
+ line-height: 1.3333;
539
+ color: var(--gpt-soft);
540
+ text-align: center;
541
+ }
542
+
543
+ /* At phone width the app drops its secondary controls and the disclaimer. */
544
+ @container (width < 460px) {
545
+ .wide,
546
+ .note,
547
+ .lights {
548
+ display: none;
549
+ }
550
+
551
+ .bubble {
552
+ max-width: 85%;
553
+ }
554
+
555
+ .dock {
556
+ padding-bottom: 12px;
557
+ }
558
+ }
559
+
560
+ /* ---------- the reveal: only opacity, a short rise and a mask move, so nothing reflows ---------- */
561
+ .turn > *,
562
+ .tool-text {
563
+ transition:
564
+ opacity var(--duration-fast) var(--ease-smooth-out),
565
+ translate var(--duration-fast) var(--ease-smooth-out);
566
+ }
567
+
568
+ .turn[data-phase="hidden"] > *,
569
+ .turn[data-phase="thinking"] > :not(.think) {
570
+ opacity: 0;
571
+ translate: 0 6px;
572
+ }
573
+
574
+ .turn[data-phase="thinking"] > .think {
575
+ opacity: 1;
576
+ translate: none;
577
+ animation: gpt-pulse calc(var(--duration-overlay) * 2.4) var(--ease-in-out) infinite;
578
+ }
579
+
580
+ .turn[data-phase="streaming"] > :is(.reply, .actions) {
581
+ animation: gpt-stream var(--gpt-stream) var(--ease-linear) both;
582
+ }
583
+
584
+ .turn[data-phase="streaming"] > .actions {
585
+ opacity: 0;
586
+ }
587
+
588
+ .reply,
589
+ .actions {
590
+ mask-image: linear-gradient(to bottom, #000 40%, transparent 60%);
591
+ mask-size: 100% 250%;
592
+ mask-position: 0 0;
593
+ }
594
+
595
+ /* The label keeps its colour; a dimmer band sweeps through it by mask. */
596
+ .turn[data-phase="running"] .tool-text {
597
+ mask-image: linear-gradient(100deg, #000 40%, rgb(0 0 0 / 0.4) 50%, #000 60%);
598
+ mask-size: 300% 100%;
599
+ animation: gpt-shimmer calc(var(--duration-overlay) * 3) var(--ease-linear) infinite;
600
+ }
601
+
602
+ @keyframes gpt-pulse {
603
+ 50% {
604
+ scale: 0.7;
605
+ }
606
+ }
607
+
608
+ @keyframes gpt-stream {
609
+ from {
610
+ mask-position: 0 100%;
611
+ }
612
+ to {
613
+ mask-position: 0 0;
614
+ }
615
+ }
616
+
617
+ @keyframes gpt-shimmer {
618
+ from {
619
+ mask-position: 100% 0;
620
+ }
621
+ to {
622
+ mask-position: 0 0;
623
+ }
624
+ }
625
+
626
+ @media (prefers-reduced-motion: reduce) {
627
+ .turn > *,
628
+ .tool-text,
629
+ .turn[data-phase] > *,
630
+ .turn[data-phase] .tool-text {
631
+ transition: none;
632
+ animation: none;
633
+ }
634
+ }
635
+ }
636
+ </style>
@@ -0,0 +1,25 @@
1
+ import type { ChatGptLabels } from "./labels.js";
2
+ import type { ChatMessage, ChatTheme } from "./types.js";
3
+ export interface ChatGptWindowProps {
4
+ /** The conversation's name, read out as the picture's label. */
5
+ title: string;
6
+ messages: ChatMessage[];
7
+ /** The picker in the bar. */
8
+ model?: string;
9
+ placeholder?: string;
10
+ theme?: ChatTheme;
11
+ /** Plays the messages in once the window is on screen. Off, it is a still. */
12
+ reveal?: boolean;
13
+ /** With `reveal`, plays each time this turns on, for a rotator. */
14
+ active?: boolean;
15
+ /** Called after the reveal ends and `hold` ms have passed. */
16
+ ondone?: () => void;
17
+ hold?: number;
18
+ /** Draws macOS's traffic lights, as the desktop app. */
19
+ frame?: boolean;
20
+ labels?: Partial<ChatGptLabels>;
21
+ class?: string;
22
+ }
23
+ declare const $$Component: import("svelte").Component<ChatGptWindowProps, {}, "">;
24
+ type $$Component = ReturnType<typeof $$Component>;
25
+ export default $$Component;