pptx-react-viewer 1.25.5 → 2.1.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 (231) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -2
  3. package/dist/AiChatPanel-RFBZWMUE.mjs +1087 -0
  4. package/dist/AiChatPanel-RFBZWMUE.mjs.br +0 -0
  5. package/dist/AiChatPanel-RFBZWMUE.mjs.gz +0 -0
  6. package/dist/AiChatPanel-VCCVHAAI.js +1089 -0
  7. package/dist/AiChatPanel-VCCVHAAI.js.br +0 -0
  8. package/dist/AiChatPanel-VCCVHAAI.js.gz +0 -0
  9. package/dist/{GLTFLoader-NMAS6VRH.mjs → GLTFLoader-374ZNTXW.mjs} +1 -1
  10. package/dist/GLTFLoader-374ZNTXW.mjs.br +0 -0
  11. package/dist/GLTFLoader-374ZNTXW.mjs.gz +0 -0
  12. package/dist/{GLTFLoader-GP55DVVS.js → GLTFLoader-Q7BHCGAW.js} +1 -1
  13. package/dist/GLTFLoader-Q7BHCGAW.js.br +0 -0
  14. package/dist/GLTFLoader-Q7BHCGAW.js.gz +0 -0
  15. package/dist/{Model3DScene-XVYOISVJ.mjs → Model3DScene-2M6HVWTE.mjs} +3 -3
  16. package/dist/Model3DScene-2M6HVWTE.mjs.br +0 -0
  17. package/dist/Model3DScene-2M6HVWTE.mjs.gz +0 -0
  18. package/dist/{Model3DScene-Y5ABLO7K.js → Model3DScene-RQIGSYGQ.js} +4 -4
  19. package/dist/Model3DScene-RQIGSYGQ.js.br +0 -0
  20. package/dist/Model3DScene-RQIGSYGQ.js.gz +0 -0
  21. package/dist/{OrbitControls-VH6KILH7.js → OrbitControls-SMYCPLXO.js} +1 -1
  22. package/dist/OrbitControls-SMYCPLXO.js.br +3 -0
  23. package/dist/OrbitControls-SMYCPLXO.js.gz +0 -0
  24. package/dist/{OrbitControls-F5XPYD2Z.mjs → OrbitControls-ZTGHPI5G.mjs} +1 -1
  25. package/dist/OrbitControls-ZTGHPI5G.mjs.br +0 -0
  26. package/dist/{OrbitControls-F5XPYD2Z.mjs.gz → OrbitControls-ZTGHPI5G.mjs.gz} +0 -0
  27. package/dist/{PowerPointViewer-CnADroqZ.d.ts → PowerPointViewer-gmU5rc7K.d.ts} +2 -2
  28. package/dist/PowerPointViewer-gmU5rc7K.d.ts.map +1 -0
  29. package/dist/{SmartArt3DScene-WQIDFZ7X.mjs → SmartArt3DScene-XCFVGCXE.mjs} +1 -1
  30. package/dist/SmartArt3DScene-XCFVGCXE.mjs.br +0 -0
  31. package/dist/SmartArt3DScene-XCFVGCXE.mjs.gz +0 -0
  32. package/dist/{SmartArt3DScene-HCQBDRAM.js → SmartArt3DScene-ZO3KFDI2.js} +1 -1
  33. package/dist/SmartArt3DScene-ZO3KFDI2.js.br +0 -0
  34. package/dist/SmartArt3DScene-ZO3KFDI2.js.gz +0 -0
  35. package/dist/{SurfaceChart3DScene-WMY5JTW4.js → SurfaceChart3DScene-2FVASQGC.js} +4 -4
  36. package/dist/SurfaceChart3DScene-2FVASQGC.js.br +0 -0
  37. package/dist/SurfaceChart3DScene-2FVASQGC.js.gz +0 -0
  38. package/dist/{SurfaceChart3DScene-RXZADECL.mjs → SurfaceChart3DScene-EGMWY4CZ.mjs} +3 -3
  39. package/dist/SurfaceChart3DScene-EGMWY4CZ.mjs.br +0 -0
  40. package/dist/SurfaceChart3DScene-EGMWY4CZ.mjs.gz +0 -0
  41. package/dist/{animation-timeline-CEJJLFqK.d.ts → animation-timeline-eb5z0SHW.d.ts} +250 -8
  42. package/dist/animation-timeline-eb5z0SHW.d.ts.map +1 -0
  43. package/dist/{chunk-FVQDRA5N.mjs → chunk-27XBWVO5.mjs} +3815 -415
  44. package/dist/chunk-27XBWVO5.mjs.br +0 -0
  45. package/dist/chunk-27XBWVO5.mjs.gz +0 -0
  46. package/dist/{chunk-M3SIFXWD.mjs → chunk-2X72MOPZ.mjs} +65 -0
  47. package/dist/chunk-2X72MOPZ.mjs.br +0 -0
  48. package/dist/chunk-2X72MOPZ.mjs.gz +0 -0
  49. package/dist/{chunk-TEN35NUX.js → chunk-46Z6MNRS.js} +3877 -455
  50. package/dist/chunk-46Z6MNRS.js.br +0 -0
  51. package/dist/chunk-46Z6MNRS.js.gz +0 -0
  52. package/dist/{chunk-QGM4M3NI.js → chunk-4VNS5WPM.js} +5 -0
  53. package/dist/chunk-4VNS5WPM.js.br +0 -0
  54. package/dist/chunk-4VNS5WPM.js.gz +0 -0
  55. package/dist/{chunk-FOMF7POV.mjs → chunk-6YXLGMTN.mjs} +2493 -1588
  56. package/dist/chunk-6YXLGMTN.mjs.br +0 -0
  57. package/dist/chunk-6YXLGMTN.mjs.gz +0 -0
  58. package/dist/chunk-7DQHWPIB.mjs +8029 -0
  59. package/dist/chunk-7DQHWPIB.mjs.br +0 -0
  60. package/dist/chunk-7DQHWPIB.mjs.gz +0 -0
  61. package/dist/chunk-BHVCED34.mjs +16664 -0
  62. package/dist/chunk-BHVCED34.mjs.br +0 -0
  63. package/dist/chunk-BHVCED34.mjs.gz +0 -0
  64. package/dist/{chunk-IPYAN574.js → chunk-CLNGAVNH.js} +2495 -1587
  65. package/dist/chunk-CLNGAVNH.js.br +0 -0
  66. package/dist/chunk-CLNGAVNH.js.gz +0 -0
  67. package/dist/{chunk-2EFVXYNA.js → chunk-CUUYYYC5.js} +65 -0
  68. package/dist/chunk-CUUYYYC5.js.br +0 -0
  69. package/dist/chunk-CUUYYYC5.js.gz +0 -0
  70. package/dist/chunk-LDF77JJD.js +8121 -0
  71. package/dist/chunk-LDF77JJD.js.br +0 -0
  72. package/dist/chunk-LDF77JJD.js.gz +0 -0
  73. package/dist/{chunk-F4WPQRZU.mjs → chunk-NGE7YQTF.mjs} +3866 -11224
  74. package/dist/chunk-NGE7YQTF.mjs.br +0 -0
  75. package/dist/chunk-NGE7YQTF.mjs.gz +0 -0
  76. package/dist/chunk-SYPHGLIB.js +16701 -0
  77. package/dist/chunk-SYPHGLIB.js.br +0 -0
  78. package/dist/chunk-SYPHGLIB.js.gz +0 -0
  79. package/dist/{chunk-7CSBUYRN.js → chunk-VHO53ZXZ.js} +2083 -940
  80. package/dist/chunk-VHO53ZXZ.js.br +0 -0
  81. package/dist/chunk-VHO53ZXZ.js.gz +0 -0
  82. package/dist/{chunk-6DZX6EAA.mjs → chunk-XGB3TDIC.mjs} +5 -1
  83. package/dist/chunk-XGB3TDIC.mjs.br +0 -0
  84. package/dist/chunk-XGB3TDIC.mjs.gz +0 -0
  85. package/dist/{chunk-L4ZADJOM.mjs → chunk-Y4OSELBT.mjs} +1295 -152
  86. package/dist/chunk-Y4OSELBT.mjs.br +0 -0
  87. package/dist/chunk-Y4OSELBT.mjs.gz +0 -0
  88. package/dist/chunk-ZRY7NLKS.js +11208 -0
  89. package/dist/chunk-ZRY7NLKS.js.br +0 -0
  90. package/dist/chunk-ZRY7NLKS.js.gz +0 -0
  91. package/dist/{dist-755SJ3ZH.js → dist-4LUVI3SW.js} +578 -566
  92. package/dist/dist-4LUVI3SW.js.br +0 -0
  93. package/dist/dist-4LUVI3SW.js.gz +0 -0
  94. package/dist/dist-J6PC54PZ.mjs +2 -0
  95. package/dist/dist-J6PC54PZ.mjs.br +0 -0
  96. package/dist/dist-J6PC54PZ.mjs.gz +0 -0
  97. package/dist/i18n.js +5 -5
  98. package/dist/i18n.js.br +0 -0
  99. package/dist/i18n.js.gz +0 -0
  100. package/dist/i18n.mjs +2 -2
  101. package/dist/i18n.mjs.br +0 -0
  102. package/dist/i18n.mjs.gz +0 -0
  103. package/dist/{index-J6IuHQ2_.d.ts → index-nAyIdGqF.d.ts} +4 -4
  104. package/dist/{index-J6IuHQ2_.d.ts.map → index-nAyIdGqF.d.ts.map} +1 -1
  105. package/dist/index.d.ts +3030 -220
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +41 -39
  108. package/dist/index.js.br +0 -0
  109. package/dist/index.js.gz +0 -0
  110. package/dist/index.mjs +8 -6
  111. package/dist/index.mjs.br +0 -0
  112. package/dist/index.mjs.gz +0 -0
  113. package/dist/{hooks-unstable.d.ts → internals.d.ts} +1323 -959
  114. package/dist/internals.d.ts.map +1 -0
  115. package/dist/{hooks-unstable.js → internals.js} +81 -80
  116. package/dist/internals.js.br +0 -0
  117. package/dist/internals.js.gz +0 -0
  118. package/dist/{hooks-unstable.mjs → internals.mjs} +6 -5
  119. package/dist/internals.mjs.br +0 -0
  120. package/dist/internals.mjs.gz +0 -0
  121. package/dist/pptx-viewer.css +1 -1
  122. package/dist/pptx-viewer.css.br +0 -0
  123. package/dist/pptx-viewer.css.gz +0 -0
  124. package/dist/{three.module-7CXEVRKB.mjs → three.module-AC7ESV4W.mjs} +1 -1
  125. package/dist/three.module-AC7ESV4W.mjs.br +0 -0
  126. package/dist/three.module-AC7ESV4W.mjs.gz +0 -0
  127. package/dist/{three.module-CPZZFD2Q.js → three.module-GNNNRIMK.js} +1 -1
  128. package/dist/three.module-GNNNRIMK.js.br +0 -0
  129. package/dist/three.module-GNNNRIMK.js.gz +0 -0
  130. package/dist/{useViewerBuildingBlocks-ll3_ncCH.d.ts → useViewerBuildingBlocks-CjureTRf.d.ts} +185 -174
  131. package/dist/useViewerBuildingBlocks-CjureTRf.d.ts.map +1 -0
  132. package/dist/viewer/index.d.ts +469 -52
  133. package/dist/viewer/index.d.ts.map +1 -1
  134. package/dist/viewer/index.js +35 -33
  135. package/dist/viewer/index.js.br +0 -0
  136. package/dist/viewer/index.js.gz +0 -0
  137. package/dist/viewer/index.mjs +8 -6
  138. package/dist/viewer/index.mjs.br +0 -0
  139. package/dist/viewer/index.mjs.gz +0 -0
  140. package/dist/{y-webrtc-6CRYHAXH.mjs → y-webrtc-DGVURJIF.mjs} +1 -1
  141. package/dist/y-webrtc-DGVURJIF.mjs.br +0 -0
  142. package/dist/y-webrtc-DGVURJIF.mjs.gz +0 -0
  143. package/dist/{y-webrtc-QCZF75JW.js → y-webrtc-YN53KW3J.js} +5 -5
  144. package/dist/y-webrtc-YN53KW3J.js.br +0 -0
  145. package/dist/y-webrtc-YN53KW3J.js.gz +0 -0
  146. package/dist/{y-websocket-QJEM6VOB.js → y-websocket-HZCIQLM6.js} +1 -1
  147. package/dist/y-websocket-HZCIQLM6.js.br +0 -0
  148. package/dist/y-websocket-HZCIQLM6.js.gz +0 -0
  149. package/dist/{y-websocket-57Q3RNHC.mjs → y-websocket-P5LTAJ5T.mjs} +1 -1
  150. package/dist/y-websocket-P5LTAJ5T.mjs.br +0 -0
  151. package/dist/y-websocket-P5LTAJ5T.mjs.gz +0 -0
  152. package/dist/{yjs-Y7IYUWOG.mjs → yjs-FKGX4QHS.mjs} +1 -1
  153. package/dist/yjs-FKGX4QHS.mjs.br +0 -0
  154. package/dist/yjs-FKGX4QHS.mjs.gz +0 -0
  155. package/dist/{yjs-UH3EPF3C.js → yjs-HBLSEMHP.js} +1 -1
  156. package/dist/yjs-HBLSEMHP.js.br +0 -0
  157. package/dist/yjs-HBLSEMHP.js.gz +0 -0
  158. package/package.json +23 -9
  159. package/dist/GLTFLoader-GP55DVVS.js.br +0 -0
  160. package/dist/GLTFLoader-GP55DVVS.js.gz +0 -0
  161. package/dist/GLTFLoader-NMAS6VRH.mjs.br +0 -0
  162. package/dist/GLTFLoader-NMAS6VRH.mjs.gz +0 -0
  163. package/dist/Model3DScene-XVYOISVJ.mjs.br +0 -0
  164. package/dist/Model3DScene-XVYOISVJ.mjs.gz +0 -0
  165. package/dist/Model3DScene-Y5ABLO7K.js.br +0 -0
  166. package/dist/Model3DScene-Y5ABLO7K.js.gz +0 -0
  167. package/dist/OrbitControls-F5XPYD2Z.mjs.br +0 -0
  168. package/dist/OrbitControls-VH6KILH7.js.br +0 -2
  169. package/dist/OrbitControls-VH6KILH7.js.gz +0 -0
  170. package/dist/PowerPointViewer-CnADroqZ.d.ts.map +0 -1
  171. package/dist/SmartArt3DScene-HCQBDRAM.js.br +0 -0
  172. package/dist/SmartArt3DScene-HCQBDRAM.js.gz +0 -0
  173. package/dist/SmartArt3DScene-WQIDFZ7X.mjs.br +0 -0
  174. package/dist/SmartArt3DScene-WQIDFZ7X.mjs.gz +0 -0
  175. package/dist/SurfaceChart3DScene-RXZADECL.mjs.br +0 -0
  176. package/dist/SurfaceChart3DScene-RXZADECL.mjs.gz +0 -0
  177. package/dist/SurfaceChart3DScene-WMY5JTW4.js.br +0 -0
  178. package/dist/SurfaceChart3DScene-WMY5JTW4.js.gz +0 -0
  179. package/dist/animation-timeline-CEJJLFqK.d.ts.map +0 -1
  180. package/dist/chunk-2EFVXYNA.js.br +0 -0
  181. package/dist/chunk-2EFVXYNA.js.gz +0 -0
  182. package/dist/chunk-6DZX6EAA.mjs.br +0 -0
  183. package/dist/chunk-6DZX6EAA.mjs.gz +0 -0
  184. package/dist/chunk-7CSBUYRN.js.br +0 -0
  185. package/dist/chunk-7CSBUYRN.js.gz +0 -0
  186. package/dist/chunk-E55Q4AC7.js +0 -18639
  187. package/dist/chunk-E55Q4AC7.js.br +0 -0
  188. package/dist/chunk-E55Q4AC7.js.gz +0 -0
  189. package/dist/chunk-F4WPQRZU.mjs.br +0 -0
  190. package/dist/chunk-F4WPQRZU.mjs.gz +0 -0
  191. package/dist/chunk-FOMF7POV.mjs.br +0 -0
  192. package/dist/chunk-FOMF7POV.mjs.gz +0 -0
  193. package/dist/chunk-FVQDRA5N.mjs.br +0 -0
  194. package/dist/chunk-FVQDRA5N.mjs.gz +0 -0
  195. package/dist/chunk-IPYAN574.js.br +0 -0
  196. package/dist/chunk-IPYAN574.js.gz +0 -0
  197. package/dist/chunk-L4ZADJOM.mjs.br +0 -0
  198. package/dist/chunk-L4ZADJOM.mjs.gz +0 -0
  199. package/dist/chunk-M3SIFXWD.mjs.br +0 -0
  200. package/dist/chunk-M3SIFXWD.mjs.gz +0 -0
  201. package/dist/chunk-QGM4M3NI.js.br +0 -0
  202. package/dist/chunk-QGM4M3NI.js.gz +0 -0
  203. package/dist/chunk-TEN35NUX.js.br +0 -0
  204. package/dist/chunk-TEN35NUX.js.gz +0 -0
  205. package/dist/dist-755SJ3ZH.js.br +0 -0
  206. package/dist/dist-755SJ3ZH.js.gz +0 -0
  207. package/dist/dist-FFWP5NQZ.mjs +0 -2
  208. package/dist/dist-FFWP5NQZ.mjs.br +0 -0
  209. package/dist/dist-FFWP5NQZ.mjs.gz +0 -0
  210. package/dist/hooks-unstable.d.ts.map +0 -1
  211. package/dist/hooks-unstable.js.br +0 -0
  212. package/dist/hooks-unstable.js.gz +0 -0
  213. package/dist/hooks-unstable.mjs.br +0 -0
  214. package/dist/hooks-unstable.mjs.gz +0 -0
  215. package/dist/three.module-7CXEVRKB.mjs.br +0 -0
  216. package/dist/three.module-7CXEVRKB.mjs.gz +0 -0
  217. package/dist/three.module-CPZZFD2Q.js.br +0 -0
  218. package/dist/three.module-CPZZFD2Q.js.gz +0 -0
  219. package/dist/useViewerBuildingBlocks-ll3_ncCH.d.ts.map +0 -1
  220. package/dist/y-webrtc-6CRYHAXH.mjs.br +0 -0
  221. package/dist/y-webrtc-6CRYHAXH.mjs.gz +0 -0
  222. package/dist/y-webrtc-QCZF75JW.js.br +0 -0
  223. package/dist/y-webrtc-QCZF75JW.js.gz +0 -0
  224. package/dist/y-websocket-57Q3RNHC.mjs.br +0 -0
  225. package/dist/y-websocket-57Q3RNHC.mjs.gz +0 -0
  226. package/dist/y-websocket-QJEM6VOB.js.br +0 -0
  227. package/dist/y-websocket-QJEM6VOB.js.gz +0 -0
  228. package/dist/yjs-UH3EPF3C.js.br +0 -0
  229. package/dist/yjs-UH3EPF3C.js.gz +0 -0
  230. package/dist/yjs-Y7IYUWOG.mjs.br +0 -0
  231. package/dist/yjs-Y7IYUWOG.mjs.gz +0 -0
@@ -0,0 +1,1089 @@
1
+ 'use strict';
2
+
3
+ var chunkSYPHGLIB_js = require('./chunk-SYPHGLIB.js');
4
+ var chunkLDF77JJD_js = require('./chunk-LDF77JJD.js');
5
+ require('./chunk-46Z6MNRS.js');
6
+ require('./chunk-CUUYYYC5.js');
7
+ require('./chunk-CLNGAVNH.js');
8
+ require('./chunk-4VNS5WPM.js');
9
+ var react = require('react');
10
+ var reactI18next = require('react-i18next');
11
+ var lu = require('react-icons/lu');
12
+ var react$1 = require('@ai-sdk/react');
13
+ var ai = require('ai');
14
+ var jsxRuntime = require('react/jsx-runtime');
15
+
16
+ // src/viewer/hooks/ai/ai-deck-id.ts
17
+ function deckIdFromBridge(bridge) {
18
+ const meta = bridge.getDeckMeta();
19
+ const title = (meta.title ?? "deck").trim().toLowerCase().replace(/\s+/gu, "-").slice(0, 60);
20
+ return `${title || "deck"}::${meta.slideCount}`;
21
+ }
22
+ function useAiChat(bridge, config) {
23
+ const configRef = react.useRef(config);
24
+ const [state, setState] = react.useState("checking");
25
+ const [session, setSession] = react.useState(null);
26
+ const [initError, setInitError] = react.useState(void 0);
27
+ react.useEffect(() => {
28
+ let cancelled = false;
29
+ setState("checking");
30
+ setSession(null);
31
+ setInitError(void 0);
32
+ void (async () => {
33
+ try {
34
+ const available = await chunkSYPHGLIB_js.isAiAvailable();
35
+ if (cancelled) {
36
+ return;
37
+ }
38
+ if (!available) {
39
+ setState("unavailable");
40
+ return;
41
+ }
42
+ const built = await chunkSYPHGLIB_js.createAiChatSession(bridge, configRef.current);
43
+ if (cancelled) {
44
+ return;
45
+ }
46
+ setSession(built);
47
+ setState("ready");
48
+ } catch (err) {
49
+ if (cancelled) {
50
+ return;
51
+ }
52
+ setInitError(err instanceof Error ? err : new Error(String(err)));
53
+ setState("error");
54
+ }
55
+ })();
56
+ return () => {
57
+ cancelled = true;
58
+ };
59
+ }, [bridge]);
60
+ return { state, session, initError };
61
+ }
62
+ var READY_TOOL_STATES = /* @__PURE__ */ new Set([
63
+ "input-available",
64
+ "output-available",
65
+ "output-error"
66
+ ]);
67
+ function extractReadyToolCalls(messages) {
68
+ const out = [];
69
+ for (const message of messages) {
70
+ for (const part of message.parts) {
71
+ if (!ai.isToolUIPart(part) && !ai.isDynamicToolUIPart(part)) {
72
+ continue;
73
+ }
74
+ const tp = part;
75
+ const toolCallId = tp.toolCallId ?? "";
76
+ if (toolCallId.length === 0 || tp.input === void 0) {
77
+ continue;
78
+ }
79
+ if (!READY_TOOL_STATES.has(tp.state ?? "")) {
80
+ continue;
81
+ }
82
+ out.push({ toolName: ai.getToolOrDynamicToolName(part), toolCallId, input: tp.input });
83
+ }
84
+ }
85
+ return out;
86
+ }
87
+ function toRenderableParts(message) {
88
+ const out = [];
89
+ for (const part of message.parts) {
90
+ if (ai.isTextUIPart(part)) {
91
+ if (part.text.trim().length > 0) {
92
+ out.push({ kind: "text", text: part.text });
93
+ }
94
+ continue;
95
+ }
96
+ if (ai.isToolUIPart(part) || ai.isDynamicToolUIPart(part)) {
97
+ const tp = part;
98
+ out.push({
99
+ kind: "tool",
100
+ toolName: ai.getToolOrDynamicToolName(part),
101
+ toolCallId: tp.toolCallId ?? "",
102
+ state: tp.state ?? "input-streaming",
103
+ input: tp.input,
104
+ output: tp.output,
105
+ errorText: tp.errorText
106
+ });
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+
112
+ // src/viewer/hooks/ai/ai-context-transport.ts
113
+ function injectDeckContext(messages, context) {
114
+ const lastUserIndex = findLastUserIndex(messages);
115
+ if (lastUserIndex < 0) {
116
+ return messages;
117
+ }
118
+ const target = messages[lastUserIndex];
119
+ const contextPart = {
120
+ type: "text",
121
+ text: `Current deck context (for your reference; do not repeat it back verbatim):
122
+
123
+ ${context}`
124
+ };
125
+ const next = messages.slice();
126
+ next[lastUserIndex] = {
127
+ ...target,
128
+ parts: [contextPart, ...target.parts]
129
+ };
130
+ return next;
131
+ }
132
+ function findLastUserIndex(messages) {
133
+ for (let i = messages.length - 1; i >= 0; i--) {
134
+ if (messages[i].role === "user") {
135
+ return i;
136
+ }
137
+ }
138
+ return -1;
139
+ }
140
+ function withDeckContext(inner, bridge, strategy) {
141
+ return {
142
+ async sendMessages(options) {
143
+ if (strategy === "none" && !bridge.getFocusedTargets) {
144
+ return inner.sendMessages(options);
145
+ }
146
+ let context = "";
147
+ try {
148
+ context = await chunkSYPHGLIB_js.buildDeckContext(bridge, { strategy, focus: {} });
149
+ } catch {
150
+ context = "";
151
+ }
152
+ if (!context.trim()) {
153
+ return inner.sendMessages(options);
154
+ }
155
+ return inner.sendMessages({
156
+ ...options,
157
+ messages: injectDeckContext(options.messages, context)
158
+ });
159
+ },
160
+ reconnectToStream(options) {
161
+ return inner.reconnectToStream(options);
162
+ }
163
+ };
164
+ }
165
+
166
+ // src/viewer/hooks/ai/useAiConversation.ts
167
+ function useAiConversation(session, config, bridge, options = {}) {
168
+ const [, forceRefresh] = react.useReducer((n) => n + 1, 0);
169
+ const chatRef = react.useRef(null);
170
+ const onToolTargetRef = react.useRef(options.onToolTarget);
171
+ onToolTargetRef.current = options.onToolTarget;
172
+ const seenToolCallsRef = react.useRef(/* @__PURE__ */ new Set());
173
+ const clientExecutes = session.clientExecutesTools;
174
+ const transport = react.useMemo(
175
+ () => withDeckContext(session.transport, bridge, config.contextStrategy ?? "outline"),
176
+ [session, bridge, config.contextStrategy]
177
+ );
178
+ const chat = react$1.useChat({
179
+ transport,
180
+ sendAutomaticallyWhen: clientExecutes ? session.sendAutomaticallyWhen : void 0,
181
+ onError: (err) => config.onError?.(err),
182
+ onToolCall: clientExecutes ? async ({ toolCall }) => {
183
+ const { toolName, toolCallId, input } = toolCall;
184
+ const current = chatRef.current;
185
+ if (!current) {
186
+ return;
187
+ }
188
+ const addToolOutput = current.addToolOutput;
189
+ try {
190
+ const output = await session.executeToolCall(toolName, input);
191
+ addToolOutput({ tool: toolName, toolCallId, output });
192
+ } catch (err) {
193
+ addToolOutput({
194
+ tool: toolName,
195
+ toolCallId,
196
+ state: "output-error",
197
+ errorText: err instanceof Error ? err.message : String(err)
198
+ });
199
+ }
200
+ forceRefresh();
201
+ } : void 0
202
+ });
203
+ chatRef.current = chat;
204
+ react.useEffect(() => {
205
+ const seen = seenToolCallsRef.current;
206
+ for (const call of extractReadyToolCalls(chat.messages)) {
207
+ if (seen.has(call.toolCallId)) {
208
+ continue;
209
+ }
210
+ seen.add(call.toolCallId);
211
+ onToolTargetRef.current?.(chunkSYPHGLIB_js.toolCanvasTarget(call.toolName, call.input));
212
+ }
213
+ }, [chat.messages]);
214
+ const send = react.useCallback(
215
+ (text) => {
216
+ const trimmed = text.trim();
217
+ if (trimmed.length === 0) {
218
+ return;
219
+ }
220
+ void chat.sendMessage({ text: trimmed });
221
+ },
222
+ [chat]
223
+ );
224
+ const applyProposal = react.useCallback(
225
+ (id) => {
226
+ session.proposals.apply(id);
227
+ forceRefresh();
228
+ },
229
+ [session]
230
+ );
231
+ const rejectProposal = react.useCallback(
232
+ (id) => {
233
+ session.proposals.revert(id);
234
+ forceRefresh();
235
+ },
236
+ [session]
237
+ );
238
+ const acceptAllProposals = react.useCallback(() => {
239
+ session.proposals.acceptAll();
240
+ forceRefresh();
241
+ }, [session]);
242
+ const setMessages = react.useCallback(
243
+ (next) => {
244
+ chat.setMessages(next);
245
+ for (const call of extractReadyToolCalls(next)) {
246
+ seenToolCallsRef.current.add(call.toolCallId);
247
+ }
248
+ session.proposals.clear();
249
+ forceRefresh();
250
+ },
251
+ [chat, session]
252
+ );
253
+ return {
254
+ messages: chat.messages,
255
+ status: chat.status,
256
+ error: chat.error,
257
+ isStreaming: chat.status === "submitted" || chat.status === "streaming",
258
+ send,
259
+ stop: () => void chat.stop(),
260
+ setMessages,
261
+ clearError: () => chat.clearError(),
262
+ proposals: session.proposals.list(),
263
+ applyProposal,
264
+ rejectProposal,
265
+ acceptAllProposals
266
+ };
267
+ }
268
+ var SAVE_DEBOUNCE_MS = 800;
269
+ var TITLE_MAX = 40;
270
+ function newChatId() {
271
+ return `chat-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
272
+ }
273
+ function deriveTitle(messages) {
274
+ for (const message of messages) {
275
+ if (message.role !== "user") {
276
+ continue;
277
+ }
278
+ const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join(" ").trim();
279
+ if (text) {
280
+ return text.length > TITLE_MAX ? `${text.slice(0, TITLE_MAX).trimEnd()}\u2026` : text;
281
+ }
282
+ }
283
+ return "";
284
+ }
285
+ function useAiHistory(input) {
286
+ const { deckId, messages, setMessages } = input;
287
+ const store = react.useMemo(() => input.store ?? chunkSYPHGLIB_js.createChatHistoryStore(), [input.store]);
288
+ const [activeChatId, setActiveChatId] = react.useState(newChatId);
289
+ const [chats, setChats] = react.useState([]);
290
+ const createdAtRef = react.useRef(Date.now());
291
+ const saveTimer = react.useRef(null);
292
+ const refresh = react.useCallback(async () => {
293
+ setChats(await store.listChats({ deckId }));
294
+ }, [store, deckId]);
295
+ react.useEffect(() => {
296
+ setActiveChatId(newChatId());
297
+ createdAtRef.current = Date.now();
298
+ void refresh();
299
+ }, [refresh]);
300
+ react.useEffect(() => {
301
+ if (messages.length === 0) {
302
+ return;
303
+ }
304
+ if (saveTimer.current) {
305
+ clearTimeout(saveTimer.current);
306
+ }
307
+ saveTimer.current = setTimeout(() => {
308
+ void (async () => {
309
+ await store.saveChat({
310
+ id: activeChatId,
311
+ title: deriveTitle(messages) || "Untitled chat",
312
+ deckId,
313
+ messages,
314
+ createdAt: createdAtRef.current,
315
+ updatedAt: Date.now()
316
+ });
317
+ await refresh();
318
+ })();
319
+ }, SAVE_DEBOUNCE_MS);
320
+ return () => {
321
+ if (saveTimer.current) {
322
+ clearTimeout(saveTimer.current);
323
+ }
324
+ };
325
+ }, [messages, activeChatId, deckId, store, refresh]);
326
+ const newChat = react.useCallback(() => {
327
+ setActiveChatId(newChatId());
328
+ createdAtRef.current = Date.now();
329
+ setMessages([]);
330
+ }, [setMessages]);
331
+ const clearCurrent = react.useCallback(() => {
332
+ setMessages([]);
333
+ }, [setMessages]);
334
+ const resumeChat = react.useCallback(
335
+ async (id) => {
336
+ const chat = await store.loadChat(id);
337
+ if (!chat) {
338
+ return;
339
+ }
340
+ setActiveChatId(chat.id);
341
+ createdAtRef.current = chat.createdAt;
342
+ setMessages(chat.messages);
343
+ },
344
+ [store, setMessages]
345
+ );
346
+ const deleteChat = react.useCallback(
347
+ async (id) => {
348
+ await store.deleteChat(id);
349
+ if (id === activeChatId) {
350
+ setActiveChatId(newChatId());
351
+ createdAtRef.current = Date.now();
352
+ setMessages([]);
353
+ }
354
+ await refresh();
355
+ },
356
+ [store, activeChatId, setMessages, refresh]
357
+ );
358
+ return { chats, activeChatId, refresh, newChat, resumeChat, clearCurrent, deleteChat };
359
+ }
360
+ function AiComposer({
361
+ isStreaming,
362
+ onSend,
363
+ onStop,
364
+ prefillText,
365
+ prefillNonce
366
+ }) {
367
+ const { t } = reactI18next.useTranslation();
368
+ const [value, setValue] = react.useState("");
369
+ const textareaRef = react.useRef(null);
370
+ const appliedNonce = react.useRef(void 0);
371
+ react.useEffect(() => {
372
+ if (prefillNonce === void 0 || prefillNonce === appliedNonce.current) {
373
+ return;
374
+ }
375
+ appliedNonce.current = prefillNonce;
376
+ if (prefillText) {
377
+ setValue(prefillText);
378
+ }
379
+ textareaRef.current?.focus();
380
+ }, [prefillNonce, prefillText]);
381
+ const submit = react.useCallback(() => {
382
+ const trimmed = value.trim();
383
+ if (trimmed.length === 0 || isStreaming) {
384
+ return;
385
+ }
386
+ onSend(trimmed);
387
+ setValue("");
388
+ }, [value, isStreaming, onSend]);
389
+ const handleKeyDown = react.useCallback(
390
+ (e) => {
391
+ if (e.key === "Enter" && !e.shiftKey) {
392
+ e.preventDefault();
393
+ submit();
394
+ }
395
+ },
396
+ [submit]
397
+ );
398
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border p-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-1.5 rounded-md border border-input bg-background px-2 py-1.5 focus-within:border-ring", children: [
399
+ /* @__PURE__ */ jsxRuntime.jsx(
400
+ "textarea",
401
+ {
402
+ ref: textareaRef,
403
+ value,
404
+ onChange: (e) => setValue(e.target.value),
405
+ onKeyDown: handleKeyDown,
406
+ rows: 1,
407
+ placeholder: t("pptx.ai.placeholder"),
408
+ "aria-label": t("pptx.ai.placeholder"),
409
+ className: "max-h-32 min-h-[1.5rem] flex-1 resize-none bg-transparent text-[13px] text-foreground outline-none placeholder:text-muted-foreground"
410
+ }
411
+ ),
412
+ isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
413
+ "button",
414
+ {
415
+ type: "button",
416
+ onClick: onStop,
417
+ title: t("pptx.ai.stop"),
418
+ "aria-label": t("pptx.ai.stop"),
419
+ className: "shrink-0 rounded-sm p-1.5 text-muted-foreground transition-colors hover:bg-accent",
420
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuSquare, { className: "w-4 h-4" })
421
+ }
422
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
423
+ "button",
424
+ {
425
+ type: "button",
426
+ onClick: submit,
427
+ disabled: value.trim().length === 0,
428
+ title: t("pptx.ai.send"),
429
+ "aria-label": t("pptx.ai.send"),
430
+ className: chunkLDF77JJD_js.cn(
431
+ "shrink-0 rounded-sm p-1.5 transition-colors",
432
+ value.trim().length === 0 ? "text-muted-foreground/50" : "bg-primary text-primary-foreground hover:bg-primary/90"
433
+ ),
434
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuSend, { className: "w-4 h-4" })
435
+ }
436
+ )
437
+ ] }) });
438
+ }
439
+ function mergeDirective(slideIndex, elementIdA, elementIdB) {
440
+ return `Merge the two selected tables (elementIdA=${elementIdA}, elementIdB=${elementIdB}) on slide ${slideIndex + 1} using merge_tables; stage it now, do not ask me to confirm.`;
441
+ }
442
+ function AiFocusBar({
443
+ targets,
444
+ slides,
445
+ isPinned,
446
+ onPin,
447
+ onClearPin,
448
+ onSendDirective,
449
+ pickMode,
450
+ hasPicks,
451
+ onStartPick,
452
+ onStopPick,
453
+ onClearPicks
454
+ }) {
455
+ const { t } = reactI18next.useTranslation();
456
+ const chips = chunkSYPHGLIB_js.focusTargetChips(targets, slides);
457
+ const twoTables = chunkSYPHGLIB_js.isTwoTableFocus(targets, slides);
458
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-secondary/30", children: [
459
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1 px-2.5 py-1.5", children: [
460
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("pptx.ai.focusScope") }),
461
+ chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(
462
+ "span",
463
+ {
464
+ className: chunkLDF77JJD_js.cn(
465
+ "inline-flex max-w-[10rem] items-center rounded-full px-2 py-0.5 text-[11px]",
466
+ hasPicks || isPinned ? "bg-primary/15 text-primary" : "bg-muted text-muted-foreground"
467
+ ),
468
+ title: chip.title,
469
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: chip.label })
470
+ },
471
+ chip.key
472
+ )),
473
+ isPinned && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/15 px-1.5 py-0.5 text-[10px] font-medium text-primary", children: t("pptx.ai.pinnedFocus") }),
474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-0.5", children: [
475
+ twoTables && /* @__PURE__ */ jsxRuntime.jsxs(
476
+ "button",
477
+ {
478
+ type: "button",
479
+ onClick: () => onSendDirective(
480
+ mergeDirective(twoTables.slideIndex, twoTables.elementIdA, twoTables.elementIdB)
481
+ ),
482
+ className: "inline-flex items-center gap-1 rounded-sm bg-primary/90 px-1.5 py-0.5 text-[11px] font-medium text-primary-foreground hover:bg-primary",
483
+ children: [
484
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuGitMerge, { className: "w-3 h-3" }),
485
+ t("pptx.ai.mergeSelectedTables")
486
+ ]
487
+ }
488
+ ),
489
+ /* @__PURE__ */ jsxRuntime.jsx(
490
+ "button",
491
+ {
492
+ type: "button",
493
+ onClick: pickMode ? onStopPick : onStartPick,
494
+ title: t("pptx.ai.pickElement"),
495
+ "aria-label": t("pptx.ai.pickAria"),
496
+ "aria-pressed": pickMode,
497
+ className: chunkLDF77JJD_js.cn(
498
+ "rounded-sm p-1",
499
+ pickMode ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-accent"
500
+ ),
501
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCrosshair, { className: "w-3.5 h-3.5" })
502
+ }
503
+ ),
504
+ hasPicks && /* @__PURE__ */ jsxRuntime.jsx(
505
+ "button",
506
+ {
507
+ type: "button",
508
+ onClick: onClearPicks,
509
+ title: t("pptx.ai.pickClear"),
510
+ "aria-label": t("pptx.ai.pickClear"),
511
+ className: "rounded-sm p-1 text-muted-foreground hover:bg-accent",
512
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { className: "w-3.5 h-3.5" })
513
+ }
514
+ ),
515
+ !hasPicks && /* @__PURE__ */ jsxRuntime.jsx(
516
+ "button",
517
+ {
518
+ type: "button",
519
+ onClick: isPinned ? onClearPin : onPin,
520
+ title: isPinned ? t("pptx.ai.clearFocus") : t("pptx.ai.pinFocus"),
521
+ "aria-label": isPinned ? t("pptx.ai.clearFocus") : t("pptx.ai.pinFocus"),
522
+ className: "rounded-sm p-1 text-muted-foreground hover:bg-accent",
523
+ children: isPinned ? /* @__PURE__ */ jsxRuntime.jsx(lu.LuPinOff, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lu.LuPin, { className: "w-3.5 h-3.5" })
524
+ }
525
+ )
526
+ ] })
527
+ ] }),
528
+ pickMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t border-primary/20 bg-primary/5 px-2.5 py-1", children: [
529
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCrosshair, { className: "w-3.5 h-3.5 shrink-0 animate-pulse text-primary" }),
530
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-primary", children: t("pptx.ai.pickElementHint") }),
531
+ /* @__PURE__ */ jsxRuntime.jsx(
532
+ "button",
533
+ {
534
+ type: "button",
535
+ onClick: onStopPick,
536
+ className: "ml-auto rounded-sm bg-primary px-2 py-0.5 text-[11px] font-medium text-primary-foreground hover:bg-primary/90",
537
+ children: t("pptx.ai.pickDone")
538
+ }
539
+ )
540
+ ] })
541
+ ] });
542
+ }
543
+
544
+ // src/viewer/components/ai/ai-relative-time.ts
545
+ function relativeTime(from, now = Date.now()) {
546
+ const seconds = Math.max(0, Math.round((now - from) / 1e3));
547
+ if (seconds < 45) {
548
+ return "just now";
549
+ }
550
+ const minutes = Math.round(seconds / 60);
551
+ if (minutes < 60) {
552
+ return `${minutes}m ago`;
553
+ }
554
+ const hours = Math.round(minutes / 60);
555
+ if (hours < 24) {
556
+ return `${hours}h ago`;
557
+ }
558
+ const days = Math.round(hours / 24);
559
+ if (days < 7) {
560
+ return `${days}d ago`;
561
+ }
562
+ const weeks = Math.round(days / 7);
563
+ return `${weeks}w ago`;
564
+ }
565
+ function AiHistoryList({ chats, activeChatId, onResume, onDelete }) {
566
+ const { t } = reactI18next.useTranslation();
567
+ if (chats.length === 0) {
568
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 py-4 text-center text-[12px] text-muted-foreground", children: t("pptx.ai.historyEmpty") });
569
+ }
570
+ return /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-64 overflow-y-auto py-1", children: chats.map((chat) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "group flex items-center gap-1 px-1", children: [
571
+ /* @__PURE__ */ jsxRuntime.jsxs(
572
+ "button",
573
+ {
574
+ type: "button",
575
+ onClick: () => onResume(chat.id),
576
+ className: chunkLDF77JJD_js.cn(
577
+ "flex min-w-0 flex-1 items-start gap-2 rounded-sm px-2 py-1.5 text-left hover:bg-accent",
578
+ chat.id === activeChatId && "bg-accent/60"
579
+ ),
580
+ children: [
581
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuMessageSquare, { className: "mt-0.5 w-3.5 h-3.5 shrink-0 text-muted-foreground" }),
582
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
583
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate text-[12px] font-medium text-foreground", children: chat.title || t("pptx.ai.untitledChat") }),
584
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block text-[10px] text-muted-foreground", children: [
585
+ relativeTime(chat.updatedAt),
586
+ " \xB7",
587
+ " ",
588
+ t("pptx.ai.messageCount", { count: chat.messageCount })
589
+ ] })
590
+ ] })
591
+ ]
592
+ }
593
+ ),
594
+ /* @__PURE__ */ jsxRuntime.jsx(
595
+ "button",
596
+ {
597
+ type: "button",
598
+ onClick: () => onDelete(chat.id),
599
+ title: t("pptx.ai.deleteChat"),
600
+ "aria-label": t("pptx.ai.deleteChat"),
601
+ className: "shrink-0 rounded-sm p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100",
602
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuTrash2, { className: "w-3.5 h-3.5" })
603
+ }
604
+ )
605
+ ] }, chat.id)) });
606
+ }
607
+ function AiHistoryMenu({
608
+ chats,
609
+ activeChatId,
610
+ onResume,
611
+ onDelete,
612
+ onNewChat,
613
+ onClose
614
+ }) {
615
+ const { t } = reactI18next.useTranslation();
616
+ const ref = react.useRef(null);
617
+ react.useEffect(() => {
618
+ const handler = (e) => {
619
+ if (ref.current && !ref.current.contains(e.target)) {
620
+ onClose();
621
+ }
622
+ };
623
+ document.addEventListener("mousedown", handler);
624
+ return () => document.removeEventListener("mousedown", handler);
625
+ }, [onClose]);
626
+ return /* @__PURE__ */ jsxRuntime.jsxs(
627
+ "div",
628
+ {
629
+ ref,
630
+ className: "absolute right-2 top-10 z-40 w-64 rounded-md border border-border bg-popover shadow-xl",
631
+ children: [
632
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-border px-2.5 py-1.5", children: [
633
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("pptx.ai.historyTitle") }),
634
+ /* @__PURE__ */ jsxRuntime.jsxs(
635
+ "button",
636
+ {
637
+ type: "button",
638
+ onClick: () => {
639
+ onNewChat();
640
+ onClose();
641
+ },
642
+ className: "inline-flex items-center gap-1 rounded-sm bg-primary/90 px-1.5 py-0.5 text-[11px] font-medium text-primary-foreground hover:bg-primary",
643
+ children: [
644
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: "w-3 h-3" }),
645
+ t("pptx.ai.newChat")
646
+ ]
647
+ }
648
+ )
649
+ ] }),
650
+ /* @__PURE__ */ jsxRuntime.jsx(
651
+ AiHistoryList,
652
+ {
653
+ chats,
654
+ activeChatId,
655
+ onResume: (id) => {
656
+ onResume(id);
657
+ onClose();
658
+ },
659
+ onDelete
660
+ }
661
+ ),
662
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "border-t border-border px-2.5 py-1.5 text-[10px] text-muted-foreground", children: t("pptx.ai.historyHint") })
663
+ ]
664
+ }
665
+ );
666
+ }
667
+
668
+ // src/viewer/components/ai/ai-theme-result.ts
669
+ var THEME_TOOLS = /* @__PURE__ */ new Set(["apply_theme_preset", "update_theme_colors", "update_theme_fonts"]);
670
+ function appliedThemeFromPart(part) {
671
+ if (!THEME_TOOLS.has(part.toolName) || part.state !== "output-available") {
672
+ return null;
673
+ }
674
+ const out = part.output;
675
+ if (!out?.applied) {
676
+ return null;
677
+ }
678
+ return { summary: out.summary ?? "Theme updated", previous: out.previous ?? {} };
679
+ }
680
+ function AiAppliedThemeCard({ summary, onUndo }) {
681
+ const { t } = reactI18next.useTranslation();
682
+ const [undone, setUndone] = react.useState(false);
683
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1.5 text-[12px]", children: [
684
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPalette, { className: "w-3.5 h-3.5 shrink-0 text-primary" }),
685
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 break-words text-foreground", children: [
686
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-primary", children: [
687
+ t("pptx.ai.applied"),
688
+ ":"
689
+ ] }),
690
+ " ",
691
+ summary
692
+ ] }),
693
+ !undone && /* @__PURE__ */ jsxRuntime.jsxs(
694
+ "button",
695
+ {
696
+ type: "button",
697
+ onClick: () => {
698
+ onUndo();
699
+ setUndone(true);
700
+ },
701
+ className: "inline-flex shrink-0 items-center gap-1 rounded-sm px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground",
702
+ children: [
703
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuUndo2, { className: "w-3 h-3" }),
704
+ t("pptx.ai.undo")
705
+ ]
706
+ }
707
+ )
708
+ ] });
709
+ }
710
+ var ICONS = {
711
+ view: lu.LuEye,
712
+ text: lu.LuType,
713
+ shape: lu.LuShapes,
714
+ theme: lu.LuPalette,
715
+ table: lu.LuTable,
716
+ slide: lu.LuLayoutTemplate,
717
+ chart: lu.LuChartColumn,
718
+ move: lu.LuMove,
719
+ delete: lu.LuTrash2,
720
+ search: lu.LuSearch,
721
+ nav: lu.LuNavigation,
722
+ animation: lu.LuFilm,
723
+ notes: lu.LuStickyNote,
724
+ tool: lu.LuWrench
725
+ };
726
+ function AiToolCallCard({ part }) {
727
+ const { t } = reactI18next.useTranslation();
728
+ const failed = part.state === "output-error";
729
+ const done = part.state === "output-available";
730
+ const running = !failed && !done;
731
+ const activity = chunkSYPHGLIB_js.describeToolActivity(part.toolName, part.input, running ? "present" : "past");
732
+ const Icon = ICONS[activity.icon] ?? lu.LuWrench;
733
+ const rawSummary = chunkSYPHGLIB_js.summarizeToolArgs(part.input);
734
+ const statusLabel = failed ? t("pptx.ai.toolFailed") : done ? t("pptx.ai.toolDone") : t("pptx.ai.toolRunning");
735
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[12px]", children: [
736
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
737
+ /* @__PURE__ */ jsxRuntime.jsx(
738
+ Icon,
739
+ {
740
+ className: chunkLDF77JJD_js.cn(
741
+ "w-3.5 h-3.5 shrink-0",
742
+ failed ? "text-destructive" : "text-muted-foreground"
743
+ )
744
+ }
745
+ ),
746
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: chunkLDF77JJD_js.cn("truncate", failed ? "text-destructive" : "text-foreground"), children: activity.label }),
747
+ /* @__PURE__ */ jsxRuntime.jsxs(
748
+ "span",
749
+ {
750
+ className: chunkLDF77JJD_js.cn(
751
+ "ml-auto inline-flex items-center gap-1 rounded-sm px-1.5 py-0.5 text-[10px]",
752
+ failed ? "bg-destructive/15 text-destructive" : done ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"
753
+ ),
754
+ children: [
755
+ running && /* @__PURE__ */ jsxRuntime.jsx(lu.LuLoaderCircle, { className: "w-3 h-3 animate-spin" }),
756
+ done && /* @__PURE__ */ jsxRuntime.jsx(lu.LuCheck, { className: "w-3 h-3" }),
757
+ failed && /* @__PURE__ */ jsxRuntime.jsx(lu.LuTriangleAlert, { className: "w-3 h-3" }),
758
+ statusLabel
759
+ ]
760
+ }
761
+ )
762
+ ] }),
763
+ failed && part.errorText && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 pl-5 text-[11px] text-destructive", children: part.errorText }),
764
+ rawSummary && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group mt-0.5 pl-5", children: [
765
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer list-none text-[10px] text-muted-foreground/70 hover:text-muted-foreground", children: t("pptx.ai.toolDetails") }),
766
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-0.5 break-words font-mono text-[10px] text-muted-foreground/80", children: [
767
+ chunkSYPHGLIB_js.toolLabel(part.toolName),
768
+ ": ",
769
+ rawSummary
770
+ ] })
771
+ ] })
772
+ ] });
773
+ }
774
+ function AiMessageList({ messages, isStreaming, bridge }) {
775
+ const { t } = reactI18next.useTranslation();
776
+ const endRef = react.useRef(null);
777
+ react.useEffect(() => {
778
+ endRef.current?.scrollIntoView({ block: "end" });
779
+ }, [messages, isStreaming]);
780
+ if (messages.length === 0) {
781
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 px-6 text-center", children: [
782
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuSparkles, { className: "w-7 h-7 text-primary/70" }),
783
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: t("pptx.ai.emptyTitle") }),
784
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground", children: t("pptx.ai.emptyHint") })
785
+ ] });
786
+ }
787
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto px-3 py-3", children: [
788
+ messages.map((message) => {
789
+ const isUser = message.role === "user";
790
+ const parts = toRenderableParts(message);
791
+ if (parts.length === 0 && !isUser) {
792
+ return null;
793
+ }
794
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
795
+ /* @__PURE__ */ jsxRuntime.jsx(
796
+ "div",
797
+ {
798
+ className: chunkLDF77JJD_js.cn(
799
+ "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
800
+ isUser ? "bg-secondary text-muted-foreground" : "bg-primary/15 text-primary"
801
+ ),
802
+ "aria-label": isUser ? t("pptx.ai.you") : t("pptx.ai.assistant"),
803
+ children: isUser ? /* @__PURE__ */ jsxRuntime.jsx(lu.LuUser, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lu.LuBot, { className: "w-3.5 h-3.5" })
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1 space-y-1.5", children: parts.map((part, i) => {
807
+ if (part.kind === "text") {
808
+ return /* @__PURE__ */ jsxRuntime.jsx(
809
+ "p",
810
+ {
811
+ className: "whitespace-pre-wrap break-words text-[13px] leading-relaxed text-foreground",
812
+ children: part.text
813
+ },
814
+ i
815
+ );
816
+ }
817
+ const applied = appliedThemeFromPart(part);
818
+ if (applied) {
819
+ return /* @__PURE__ */ jsxRuntime.jsx(
820
+ AiAppliedThemeCard,
821
+ {
822
+ summary: applied.summary,
823
+ onUndo: () => bridge.applyTheme(applied.previous)
824
+ },
825
+ part.toolCallId || i
826
+ );
827
+ }
828
+ return /* @__PURE__ */ jsxRuntime.jsx(AiToolCallCard, { part }, part.toolCallId || i);
829
+ }) })
830
+ ] }, message.id);
831
+ }),
832
+ isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pl-8 text-[12px] text-muted-foreground", children: [
833
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-1", children: [
834
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:-0.2s]" }),
835
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:-0.1s]" }),
836
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground" })
837
+ ] }),
838
+ t("pptx.ai.thinking")
839
+ ] }),
840
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: endRef })
841
+ ] });
842
+ }
843
+ function AiProposalCard({ proposal, onAccept, onReject }) {
844
+ const { t } = reactI18next.useTranslation();
845
+ const lines = proposal.summary.map(chunkSYPHGLIB_js.humanizeDiffLine);
846
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-primary/40 bg-primary/5 p-2.5", children: [
847
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5 text-[11px] font-semibold uppercase tracking-wide text-primary", children: t("pptx.ai.proposedChange") }),
848
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[12px] font-medium text-foreground", children: proposal.label }),
849
+ lines.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 max-h-40 space-y-0.5 overflow-y-auto text-[11px] text-muted-foreground", children: lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "break-words", children: line }, i)) }),
850
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
851
+ /* @__PURE__ */ jsxRuntime.jsxs(
852
+ "button",
853
+ {
854
+ type: "button",
855
+ onClick: () => onAccept(proposal.id),
856
+ className: "inline-flex items-center gap-1 rounded-sm bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground transition-colors hover:bg-primary/90",
857
+ children: [
858
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCheck, { className: "w-3.5 h-3.5" }),
859
+ t("pptx.ai.accept")
860
+ ]
861
+ }
862
+ ),
863
+ /* @__PURE__ */ jsxRuntime.jsxs(
864
+ "button",
865
+ {
866
+ type: "button",
867
+ onClick: () => onReject(proposal.id),
868
+ className: "inline-flex items-center gap-1 rounded-sm border border-border px-2 py-1 text-[11px] text-muted-foreground transition-colors hover:bg-accent",
869
+ children: [
870
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { className: "w-3.5 h-3.5" }),
871
+ t("pptx.ai.reject")
872
+ ]
873
+ }
874
+ )
875
+ ] })
876
+ ] });
877
+ }
878
+ function AiConversation({ session, config, bridge, aiPanel, deckId }) {
879
+ const { t } = reactI18next.useTranslation();
880
+ const chat = useAiConversation(session, config, bridge, {
881
+ // Live "AI as a collaborator" focus: as each tool runs, navigate to and
882
+ // highlight the slide / element(s) it touches so the canvas mirrors the
883
+ // assistant in real time (and colour edits tween while it is active).
884
+ onToolTarget: (target) => {
885
+ if (target && target.slideIndex !== void 0) {
886
+ bridge.goToSlide(target.slideIndex);
887
+ }
888
+ aiPanel.flashToolTarget(target);
889
+ }
890
+ });
891
+ const history = useAiHistory({
892
+ deckId,
893
+ messages: chat.messages,
894
+ setMessages: chat.setMessages
895
+ });
896
+ const [historyOpen, setHistoryOpen] = react.useState(false);
897
+ const { showChangeBatch } = aiPanel;
898
+ react.useEffect(() => {
899
+ return session.changeAnimator.subscribe((batch) => {
900
+ if (batch) {
901
+ bridge.goToSlide(batch.slideIndex);
902
+ }
903
+ showChangeBatch(batch);
904
+ });
905
+ }, [session, bridge, showChangeBatch]);
906
+ const hasPicks = aiPanel.pickTargets.length > 0;
907
+ const effectiveTargets = hasPicks ? aiPanel.pickTargets : aiPanel.pinnedFocus ?? aiPanel.liveFocusTargets;
908
+ const isPinned = !hasPicks && aiPanel.pinnedFocus !== null;
909
+ const applyProposal = (id) => {
910
+ aiPanel.flashToolTarget(null);
911
+ chat.applyProposal(id);
912
+ };
913
+ const acceptAllProposals = () => {
914
+ aiPanel.flashToolTarget(null);
915
+ chat.acceptAllProposals();
916
+ };
917
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
918
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 border-b border-border px-2 py-1", children: [
919
+ /* @__PURE__ */ jsxRuntime.jsxs(
920
+ "button",
921
+ {
922
+ type: "button",
923
+ onClick: () => setHistoryOpen((p) => !p),
924
+ className: "inline-flex items-center gap-1 rounded-sm px-1.5 py-1 text-[12px] text-muted-foreground hover:bg-accent",
925
+ children: [
926
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuHistory, { className: "w-3.5 h-3.5" }),
927
+ t("pptx.ai.chats")
928
+ ]
929
+ }
930
+ ),
931
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-0.5", children: [
932
+ /* @__PURE__ */ jsxRuntime.jsx(
933
+ "button",
934
+ {
935
+ type: "button",
936
+ onClick: history.newChat,
937
+ title: t("pptx.ai.newChat"),
938
+ "aria-label": t("pptx.ai.newChat"),
939
+ className: "rounded-sm p-1 text-muted-foreground hover:bg-accent",
940
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuMessageSquarePlus, { className: "w-3.5 h-3.5" })
941
+ }
942
+ ),
943
+ /* @__PURE__ */ jsxRuntime.jsx(
944
+ "button",
945
+ {
946
+ type: "button",
947
+ onClick: history.clearCurrent,
948
+ title: t("pptx.ai.clearChat"),
949
+ "aria-label": t("pptx.ai.clearChat"),
950
+ disabled: chat.messages.length === 0,
951
+ className: "rounded-sm p-1 text-muted-foreground hover:bg-accent disabled:opacity-40",
952
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuTrash2, { className: "w-3.5 h-3.5" })
953
+ }
954
+ )
955
+ ] })
956
+ ] }),
957
+ historyOpen && /* @__PURE__ */ jsxRuntime.jsx(
958
+ AiHistoryMenu,
959
+ {
960
+ chats: history.chats,
961
+ activeChatId: history.activeChatId,
962
+ onResume: (id) => void history.resumeChat(id),
963
+ onDelete: (id) => void history.deleteChat(id),
964
+ onNewChat: history.newChat,
965
+ onClose: () => setHistoryOpen(false)
966
+ }
967
+ ),
968
+ /* @__PURE__ */ jsxRuntime.jsx(
969
+ AiFocusBar,
970
+ {
971
+ targets: effectiveTargets,
972
+ slides: bridge.getSlides(),
973
+ isPinned,
974
+ onPin: aiPanel.pinFocus,
975
+ onClearPin: aiPanel.clearPinnedFocus,
976
+ onSendDirective: chat.send,
977
+ pickMode: aiPanel.pickMode,
978
+ hasPicks,
979
+ onStartPick: aiPanel.startPicking,
980
+ onStopPick: aiPanel.stopPicking,
981
+ onClearPicks: aiPanel.clearPicks
982
+ }
983
+ ),
984
+ /* @__PURE__ */ jsxRuntime.jsx(AiMessageList, { messages: chat.messages, isStreaming: chat.isStreaming, bridge }),
985
+ chat.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-3 mb-2 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/5 px-2.5 py-1.5 text-[12px] text-destructive", children: [
986
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuTriangleAlert, { className: "mt-0.5 w-3.5 h-3.5 shrink-0" }),
987
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
988
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: t("pptx.ai.errorPrefix") }),
989
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-24 overflow-y-auto break-words text-[11px] opacity-80", children: chat.error.message })
990
+ ] }),
991
+ /* @__PURE__ */ jsxRuntime.jsx(
992
+ "button",
993
+ {
994
+ type: "button",
995
+ onClick: chat.clearError,
996
+ className: "shrink-0 rounded-sm px-1.5 py-0.5 text-[11px] underline-offset-2 hover:underline",
997
+ children: t("pptx.ai.retry")
998
+ }
999
+ )
1000
+ ] }),
1001
+ chat.proposals.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-[38%] space-y-2 overflow-y-auto border-t border-border bg-background px-3 py-2", children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1003
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("pptx.ai.pendingChanges", { count: chat.proposals.length }) }),
1004
+ chat.proposals.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1005
+ "button",
1006
+ {
1007
+ type: "button",
1008
+ onClick: acceptAllProposals,
1009
+ className: "rounded-sm bg-primary/90 px-2 py-0.5 text-[11px] font-medium text-primary-foreground hover:bg-primary",
1010
+ children: t("pptx.ai.acceptAll")
1011
+ }
1012
+ )
1013
+ ] }),
1014
+ chat.proposals.map((proposal) => /* @__PURE__ */ jsxRuntime.jsx(
1015
+ AiProposalCard,
1016
+ {
1017
+ proposal,
1018
+ onAccept: applyProposal,
1019
+ onReject: chat.rejectProposal
1020
+ },
1021
+ proposal.id
1022
+ ))
1023
+ ] }),
1024
+ /* @__PURE__ */ jsxRuntime.jsx(
1025
+ AiComposer,
1026
+ {
1027
+ isStreaming: chat.isStreaming,
1028
+ onSend: chat.send,
1029
+ onStop: chat.stop,
1030
+ prefillText: aiPanel.prefill.text,
1031
+ prefillNonce: aiPanel.prefill.nonce
1032
+ }
1033
+ )
1034
+ ] });
1035
+ }
1036
+ function AiChatPanel({
1037
+ bridge,
1038
+ config,
1039
+ aiPanel,
1040
+ onClose,
1041
+ panelWidth
1042
+ }) {
1043
+ const { t } = reactI18next.useTranslation();
1044
+ const { state, session, initError } = useAiChat(bridge, config);
1045
+ const deckId = react.useMemo(() => deckIdFromBridge(bridge), [bridge]);
1046
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1047
+ "div",
1048
+ {
1049
+ "data-pptx-ai-panel": "",
1050
+ className: "absolute right-0 top-0 z-30 flex h-full w-80 flex-col border-l border-border bg-card shadow-xl max-md:inset-x-0 max-md:top-auto max-md:bottom-0 max-md:h-[75dvh] max-md:w-full max-md:rounded-t-2xl max-md:border-l-0 max-md:border-t max-md:shadow-2xl",
1051
+ style: panelWidth ? { width: panelWidth } : void 0,
1052
+ children: [
1053
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
1054
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuSparkles, { className: "w-4 h-4 text-primary" }),
1055
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-foreground", children: t("pptx.ai.title") }),
1056
+ /* @__PURE__ */ jsxRuntime.jsx(
1057
+ "button",
1058
+ {
1059
+ type: "button",
1060
+ onClick: onClose,
1061
+ title: t("pptx.ai.close"),
1062
+ "aria-label": t("pptx.ai.close"),
1063
+ className: "ml-auto rounded-sm p-1 text-muted-foreground transition-colors hover:bg-accent",
1064
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { className: "w-4 h-4" })
1065
+ }
1066
+ )
1067
+ ] }),
1068
+ state === "checking" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuLoaderCircle, { className: "w-5 h-5 animate-spin" }) }),
1069
+ (state === "unavailable" || state === "error") && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 px-6 text-center", children: [
1070
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuTriangleAlert, { className: "w-6 h-6 text-muted-foreground" }),
1071
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: t("pptx.ai.unavailableTitle") }),
1072
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground", children: initError?.message ?? t("pptx.ai.unavailableHint") })
1073
+ ] }),
1074
+ state === "ready" && session && /* @__PURE__ */ jsxRuntime.jsx(
1075
+ AiConversation,
1076
+ {
1077
+ session,
1078
+ config,
1079
+ bridge,
1080
+ aiPanel,
1081
+ deckId
1082
+ }
1083
+ )
1084
+ ]
1085
+ }
1086
+ );
1087
+ }
1088
+
1089
+ module.exports = AiChatPanel;