pindeck 2.8.1 → 3.0.0-beta.1

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.
package/public/editor.js CHANGED
@@ -1,4 +1,4 @@
1
- /* PinDeck UI: slide list, canvas with pins, chat with Claude Code. */
1
+ /* PinDeck UI: slide list, canvas with pins, chat with the selected assistant. */
2
2
 
3
3
  const TOKEN = document.querySelector('meta[name="editor-token"]').content;
4
4
  const $ = (id) => document.getElementById(id);
@@ -261,7 +261,7 @@ slideList.addEventListener("drop", (e) => {
261
261
  async function moveSlideTo(from, to) { // to: insertion index in the current list
262
262
  const s = state.slides[from];
263
263
  if (!s) return;
264
- if (state.busy) return toast("Claude is working. Wait until it has finished.");
264
+ if (state.busy) return toast("The assistant is working. Wait until it has finished.");
265
265
  flushNudge();
266
266
  await editChain;
267
267
  const res = await api("move-slide", { eid: s.eid, before: state.slides[to]?.eid ?? null });
@@ -391,7 +391,7 @@ const EYE_OFF_ICON = `<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><pa
391
391
  async function toggleHidden(index = state.current) {
392
392
  const s = state.slides[index];
393
393
  if (!s) return;
394
- if (state.busy) return toast("Claude is working. Wait until it has finished.");
394
+ if (state.busy) return toast("The assistant is working. Wait until it has finished.");
395
395
  flushNudge();
396
396
  await editChain;
397
397
  const res = await api("slide-hidden", { eid: s.eid, hidden: !s.hidden });
@@ -497,7 +497,7 @@ function shortEid(p) {
497
497
  function renderPinTray() {
498
498
  const sel = state.sel;
499
499
  $("pin-tray").innerHTML = (sel ? `
500
- <span class="chip sel" data-sel title="Selected: ${esc(sel.eid)}. Claude sees it as “this”.">
500
+ <span class="chip sel" data-sel title="Selected: ${esc(sel.eid)}. The assistant sees it as “this”.">
501
501
  <span class="n">▢</span> S${sel.slide.index + 1} · ${esc(sel.eid.replace(/^[^/]+\//, ""))} <span class="dim">${sel.box.w} × ${sel.box.h}</span>
502
502
  <button class="x" data-unsel aria-label="Deselect">×</button></span>` : "") + state.pins.map((p) => `
503
503
  <span class="chip" data-n="${p.n}" title="${esc(p.eid)}${p.text ? " — " + esc(p.text.slice(0, 80)) : ""}">
@@ -573,7 +573,7 @@ function entryHtml(e) {
573
573
  if (t?.undone) parts.push("<span>undone</span>");
574
574
  } else if (!e.error) parts.push("No file changes");
575
575
  if (e.restored?.length) parts.push(`<span class="warn">${esc(e.restored.join(", "))} is locked; the change was reverted.</span>`);
576
- if (e.denials?.length) parts.push(`<span class="warn" title="PinDeck lets Claude run only its own commands (layout, shot, slides, pdf) and edit index.html and theme.css">` +
576
+ if (e.denials?.length) parts.push(`<span class="warn" title="PinDeck lets the assistant run only its own commands (layout, shot, slides, pdf) and edit index.html and theme.css">` +
577
577
  `Not allowed in PinDeck, so skipped: ${e.denials.map((d) => esc(`${d.name} ${d.detail}`)).join(", ")}</span>`);
578
578
  if (e.duration) parts.push(`<span class="meta">${(e.duration / 1000).toFixed(1)} s</span>`);
579
579
  return parts.join(" ");
@@ -626,7 +626,7 @@ $("diff-close").onclick = () => $("diff-dialog").close();
626
626
 
627
627
  function setBusy(busy) {
628
628
  state.busy = busy;
629
- $("notes").readOnly = busy; // hand edits wait until Claude has finished
629
+ $("notes").readOnly = busy; // hand edits wait until the assistant has finished
630
630
  $("working").hidden = !busy;
631
631
  main?.bridge.setEditable(!busy);
632
632
  $("send").hidden = busy;
@@ -668,7 +668,7 @@ async function send() {
668
668
  $("send").onclick = send;
669
669
  $("stop").onclick = () => api("stop", {});
670
670
  $("new-chat").onclick = async () => {
671
- if (!confirm("Start a new conversation? Claude forgets this chat; your slides stay as they are.")) return;
671
+ if (!confirm("Start a new conversation? The assistant forgets this chat; your slides stay as they are.")) return;
672
672
  await api("new-chat", {});
673
673
  };
674
674
  function autosize() {
@@ -723,8 +723,13 @@ function applyStatus(s) {
723
723
  setBusy(!!s.busy);
724
724
  $("model").textContent = modelLabel(s);
725
725
  $("model-btn").disabled = !!s.busy;
726
+ $("model-btn").title = `${providerLabel(s.settings?.provider)} model`;
727
+ renderEffortMenu();
728
+ $("new-chat").disabled = !!s.busy;
729
+ $("stop").hidden = !s.busy || !!s.switching;
730
+ $("working-text").textContent = s.switching ? "Updating model…" : "The assistant is working…";
726
731
  $("conn").classList.toggle("on", !!s.running);
727
- $("conn").title = s.running ? "Claude Code is running" : "Claude Code starts with the next message";
732
+ $("conn").title = s.running ? "The selected backend is running" : "The selected backend starts with the next message";
728
733
  $("add-shot").disabled = !s.chrome;
729
734
  $("add-shot").title = s.chrome ? "" : "Screenshots need Chrome or Chromium";
730
735
  $("add-slide-btn").disabled = !!s.busy;
@@ -793,7 +798,7 @@ $("add-slide-menu").addEventListener("click", async (e) => {
793
798
  if (!item) return;
794
799
  closeMenus();
795
800
  if (item.dataset.slide === "import") return $("import-file").click();
796
- if (state.busy) return toast("Claude is working. Wait until it has finished.");
801
+ if (state.busy) return toast("The assistant is working. Wait until it has finished.");
797
802
  flushNudge();
798
803
  await editChain;
799
804
  const after = state.slides[state.current]?.eid;
@@ -837,11 +842,11 @@ function importStatus(msg, error = false) {
837
842
  const importChecks = () => [...$("import-list").querySelectorAll("input[type=checkbox]")];
838
843
  function updateImportButton() {
839
844
  const n = importChecks().filter((c) => c.checked).length;
840
- // Claude's help with what the import only approximates (diagrams as pictures, lost build steps, ...)
845
+ // The assistant's help with what the import only approximates (diagrams as pictures, lost build steps, ...)
841
846
  const complex = importChecks().filter((c) => c.checked && importing.slides.find((s) => s.n === +c.value)?.complex.length).length;
842
847
  $("import-refine-wrap").hidden = !complex;
843
- $("import-refine-label").textContent = complex === 1 ? "Then ask Claude to improve the complex slide"
844
- : `Then ask Claude to improve the ${complex} complex slides`;
848
+ $("import-refine-label").textContent = complex === 1 ? "Then ask the assistant to improve the complex slide"
849
+ : `Then ask the assistant to improve the ${complex} complex slides`;
845
850
  $("import-go").textContent = n === 1 ? "Import 1 slide" : `Import ${n} slides`;
846
851
  $("import-go").disabled = !n;
847
852
  $("import-all").textContent = n === importChecks().length ? "Select none" : "Select all";
@@ -866,7 +871,7 @@ function showImportThumbs(ev) {
866
871
  $("import-dialog").addEventListener("close", async () => {
867
872
  if ($("import-dialog").returnValue !== "import" || !importing) return;
868
873
  const slides = importChecks().filter((c) => c.checked).map((c) => +c.value);
869
- if (state.busy) return toast("Claude is working. Wait until it has finished.");
874
+ if (state.busy) return toast("The assistant is working. Wait until it has finished.");
870
875
  flushNudge();
871
876
  await editChain;
872
877
  toast(`Importing ${slides.length} slide${slides.length > 1 ? "s" : ""}…`, { ms: 60000 });
@@ -875,46 +880,95 @@ $("import-dialog").addEventListener("close", async () => {
875
880
  const d = await res.json().catch(() => ({}));
876
881
  if (!res.ok) return toast(d.error || "The import failed.");
877
882
  toast(`Imported ${d.count} slide${d.count > 1 ? "s" : ""}.` + (d.missing ? ` ${d.missing} diagram${d.missing > 1 ? "s" : ""} could not be pictured (no PowerPoint or Keynote).` : "") +
878
- (d.refining ? ` Claude is improving ${d.refining === 1 ? "the complex slide" : `${d.refining} complex slides`}.` : ""), { ms: 5000 });
883
+ (d.refining ? ` The assistant is improving ${d.refining === 1 ? "the complex slide" : `${d.refining} complex slides`}.` : ""), { ms: 5000 });
879
884
  state.current = d.index;
880
885
  try { localStorage.setItem("editor.slide", String(d.index)); } catch {}
881
886
  loadMain();
882
887
  });
883
888
 
884
889
  /* ---- Model and effort --------------------------------------------------------------------------------- */
885
- const MODEL_CHOICES = [["opus", "Opus", "most capable, for layout and design work"], ["sonnet", "Sonnet", "faster, lighter on usage limits"]];
886
- const EFFORT_CHOICES = [["low", "Low", "quick edits"], ["medium", "Medium", ""], ["high", "High", ""],
887
- ["xhigh", "Extra high", ""], ["max", "Max", "slowest, most careful"]];
888
- function prettyModel(id) {
889
- const m = /claude-(\w+)-(\d+)(?:-(\d+))?/.exec(id || "");
890
- return m ? `${m[1][0].toUpperCase()}${m[1].slice(1)} ${m[2]}${m[3] && m[3].length <= 2 ? "." + m[3] : ""}` : id || "";
891
- }
890
+ let modelCatalog = {};
891
+ let showAllModels = false;
892
+ try { showAllModels = localStorage.getItem("editor.showAllModels") === "true"; } catch {}
893
+ const providerLabel = p => p === "codex" ? "OpenAI / ChatGPT" : "Claude";
892
894
  function modelLabel(s) {
893
895
  const st = s.settings || {};
894
- // the full name (e.g. "Sonnet 5.5") once Claude Code has reported it for the chosen model, else the choice
895
- const full = prettyModel(s.model);
896
- const model = full && (!st.model || full.toLowerCase().startsWith(st.model)) ? full : MODEL_CHOICES.find((c) => c[0] === st.model)?.[1] || full;
897
- return [model, st.effort ? EFFORT_CHOICES.find((c) => c[0] === st.effort)?.[1].toLowerCase() + " effort" : ""].filter(Boolean).join(" · ");
896
+ const model = (s.models?.[st.provider]?.models || []).find(m => m.id === st.model);
897
+ return model?.label || st.model || providerLabel(st.provider);
898
898
  }
899
899
  function renderModelMenu() {
900
900
  const st = state.status.settings || {};
901
- const opt = (key, [value, label, desc]) => `<button class="opt" role="menuitemradio" aria-checked="${st[key] === value}"
902
- data-set="${key}" data-value="${value}"><span class="tick">${st[key] === value ? "✓" : ""}</span>
903
- <strong>${label}</strong>${desc ? `<span class="d muted">${desc}</span>` : ""}</button>`;
904
- $("model-menu").innerHTML = `<div class="group">Model</div>${MODEL_CHOICES.map((c) => opt("model", c)).join("")}
905
- <div class="sep"></div><div class="group">Effort: how long Claude thinks</div>${EFFORT_CHOICES.map((c) => opt("effort", c)).join("")}`;
901
+ modelCatalog = state.status.models || modelCatalog;
902
+ const groups = ["claude", "codex"].map(provider => {
903
+ const group = modelCatalog[provider] || {};
904
+ const visible = (group.models || []).filter(m => showAllModels || m.curated || (st.provider === provider && st.model === m.id));
905
+ return `<div class="group">${providerLabel(provider)}</div>` +
906
+ visible.map(m => `<button class="opt" role="menuitemradio" aria-checked="${st.provider === provider && st.model === m.id}"
907
+ data-provider="${provider}" data-model="${esc(m.id)}"><span class="tick">${st.provider === provider && st.model === m.id ? "✓" : ""}</span>
908
+ <strong>${esc(m.label)}${!showAllModels && !m.curated ? " (current)" : ""}</strong><span class="d muted">${esc(m.description)}</span></button>`).join("") +
909
+ (!visible.length ? `<div class="muted">${esc(group.error || (group.models?.length ? "No models match this filter. Choose Show all available models below." : "No models available. Try Refresh models."))}</div>` : "");
910
+ });
911
+ $("model-menu").innerHTML = groups.join('<div class="sep"></div>') +
912
+ `<div class="sep"></div><button data-model-filter>${showAllModels ? 'Show curated models' : 'Show all available models'}</button><button data-refresh>Refresh models</button>`;
906
913
  }
907
- wireMenu("model-btn", "model-menu", renderModelMenu);
908
- $("model-menu").addEventListener("click", async (e) => {
909
- const b = e.target.closest("[data-set]");
910
- if (!b) return;
914
+ const effortLabel = value => ({ none: "None", minimal: "Minimal", low: "Low", medium: "Medium", high: "High", xhigh: "Extra high", max: "Max", ultra: "Ultra" }[value] || value);
915
+ function renderEffortMenu() {
916
+ const st = state.status.settings || {};
917
+ const model = state.status.models?.[st.provider]?.models?.find(m => m.id === st.model);
918
+ const efforts = model?.efforts || [];
919
+ $("effort").textContent = efforts.length ? effortLabel(st.effort) || "Effort" : "Effort N/A";
920
+ $("effort-btn").disabled = !!state.status.busy || !efforts.length;
921
+ $("effort-btn").title = efforts.length ? `Reasoning effort: ${effortLabel(st.effort)}` : "No adjustable reasoning effort reported for this model";
922
+ $("effort-menu").innerHTML = '<div class="group">Reasoning effort</div>' + efforts.map(value => {
923
+ const hint = value === model.defaultEffort ? (model.effortDefaultSource === "provider" ? "Model default" : "PinDeck default") : "";
924
+ const description = model.effortDescriptions?.[value] || "";
925
+ return `<button class="opt" role="menuitemradio" aria-checked="${st.effort === value}" data-effort="${esc(value)}"><span class="tick">${st.effort === value ? "✓" : ""}</span><strong>${esc(effortLabel(value))}${hint ? ` · ${hint}` : ""}</strong>${description ? `<span class="d muted">${esc(description)}</span>` : ""}</button>`;
926
+ }).join("");
927
+ }
928
+ wireMenu("effort-btn", "effort-menu", renderEffortMenu);
929
+ $("effort-menu").addEventListener("click", async e => {
930
+ const button = e.target.closest("[data-effort]");
931
+ if (!button || state.status.busy) return;
911
932
  e.stopPropagation();
912
- const res = await api("settings", { [b.dataset.set]: b.dataset.value });
933
+ try { await setModelChoice({ effort: button.dataset.effort }); } catch (e) { toast(e.message); }
934
+ });
935
+ wireMenu("model-btn", "model-menu", renderModelMenu);
936
+ async function setModelChoice(choice) {
937
+ const res = await api("settings", choice);
913
938
  const d = await res.json().catch(() => ({}));
914
- if (!res.ok) return toast(d.error || "Could not change the setting.");
915
- state.status.settings = d.settings;
916
- renderModelMenu();
917
- $("model").textContent = modelLabel(state.status);
939
+ if (!res.ok) return toast(d.error || "Could not change the model.");
940
+ state.status.settings = d.settings; renderModelMenu(); renderEffortMenu(); $("model").textContent = modelLabel(state.status);
941
+ }
942
+ let pendingModel = null;
943
+ $("model-menu").addEventListener("click", async e => {
944
+ const b = e.target.closest("button"); if (!b) return;
945
+ e.stopPropagation();
946
+ if (b.hasAttribute("data-model-filter")) {
947
+ showAllModels = !showAllModels;
948
+ try { localStorage.setItem("editor.showAllModels", String(showAllModels)); } catch {}
949
+ renderModelMenu(); return;
950
+ }
951
+ if (b.hasAttribute("data-refresh")) {
952
+ b.disabled = true;
953
+ try { const r = await api("models"); if (!r.ok) throw new Error("Model discovery failed."); state.status.models = await r.json(); renderModelMenu(); renderEffortMenu(); }
954
+ catch (e) { toast(e.message); b.disabled = false; }
955
+ return;
956
+ }
957
+ if (state.status.busy) return;
958
+ const st = state.status.settings || {};
959
+ const choice = b.dataset.effort ? { effort: b.dataset.effort } : { provider: b.dataset.provider, model: b.dataset.model };
960
+ if (choice.provider && choice.provider !== st.provider) {
961
+ pendingModel = choice;
962
+ $("provider-switch-title").textContent = `Switch to ${providerLabel(choice.provider)}?`;
963
+ $("provider-switch").returnValue = "";
964
+ $("provider-switch").showModal();
965
+ } else { try { await setModelChoice(choice); } catch (e) { toast(e.message); } }
966
+ });
967
+ $("provider-switch").addEventListener("close", async () => {
968
+ const choice = pendingModel; pendingModel = null;
969
+ const handover = $("provider-switch").returnValue;
970
+ if (!choice || !["summary", "fresh"].includes(handover)) return;
971
+ try { await setModelChoice({ ...choice, handover }); } catch (e) { toast(e.message); }
918
972
  });
919
973
 
920
974
  /* ---- Delete the selected element ---------------------------------------------------------------------
@@ -923,7 +977,7 @@ $("model-menu").addEventListener("click", async (e) => {
923
977
  let deleting = false;
924
978
  async function requestDelete(eid) {
925
979
  if (deleting || !eid) return;
926
- if (state.busy) return toast("Claude is working. Wait until it has finished.");
980
+ if (state.busy) return toast("The assistant is working. Wait until it has finished.");
927
981
  deleting = true;
928
982
  try {
929
983
  flushNudge();
@@ -964,7 +1018,7 @@ async function requestDelete(eid) {
964
1018
  }
965
1019
 
966
1020
  /* ---- Attachments: figures, audio and PDFs for the next message ----------------------------------------
967
- Files are uploaded at once into <deck>/figures/ (PDFs, reading material for Claude, outside the deck). Images get their pixel size, audio its duration and a
1021
+ Files are uploaded at once into <deck>/figures/ (PDFs, reading material for the assistant, outside the deck). Images get their pixel size, audio its duration and a
968
1022
  waveform SVG (bars, viewBox 1000 x 120, like the template's audio player), all computed here in the
969
1023
  browser. Removing an attachment before sending deletes the uploaded file again. */
970
1024
  let attachments = [], attachSeq = 0;
package/public/index.html CHANGED
@@ -45,7 +45,7 @@
45
45
  <button id="select-toggle" class="toggle" aria-pressed="true"
46
46
  title="Select (V): click to select, drag to move, handles to resize, arrows to nudge (Shift: 10 px), Alt: no snapping. Double-click or Enter to edit text.">
47
47
  <svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 2l9 5-4 1.2L6.3 13z"/></svg> Select <kbd>V</kbd></button>
48
- <button id="pin-toggle" class="toggle" title="Pin (P): click elements to pin them for Claude, Esc to stop" aria-pressed="false">
48
+ <button id="pin-toggle" class="toggle" title="Pin (P): click elements to pin them for the assistant, Esc to stop" aria-pressed="false">
49
49
  <span class="dot">1</span> Pin <kbd>P</kbd></button>
50
50
  </span>
51
51
  <span class="menu-wrap">
@@ -73,16 +73,20 @@
73
73
  </section>
74
74
  </main>
75
75
 
76
- <!-- Right: chat with Claude -->
77
- <aside class="chat-pane" aria-label="Claude">
76
+ <!-- Right: chat with the selected assistant -->
77
+ <aside class="chat-pane" aria-label="Assistant">
78
78
  <div class="pane-head chat-head">
79
79
  <span class="menu-wrap">
80
80
  <button id="model-btn" class="model-btn" aria-haspopup="menu" aria-expanded="false"
81
- title="Model and effort for Claude">Claude <span id="model" class="muted"></span> ▾</button>
81
+ title="Choose model"><span id="model" class="muted"></span> ▾</button>
82
82
  <div id="model-menu" class="menu left model-menu" role="menu" hidden></div>
83
83
  </span>
84
+ <span class="menu-wrap effort-wrap">
85
+ <button id="effort-btn" class="model-btn" aria-label="Reasoning effort" aria-haspopup="menu" aria-expanded="false"><span id="effort">Effort</span> ▾</button>
86
+ <div id="effort-menu" class="menu model-menu" role="menu" aria-label="Reasoning effort" hidden></div>
87
+ </span>
84
88
  <span class="head-actions">
85
- <span id="conn" class="conn" title="Claude Code session"></span>
89
+ <span id="conn" class="conn" title="Backend session"></span>
86
90
  <button id="new-chat" class="small" title="Start a new conversation">New chat</button>
87
91
  </span>
88
92
  </div>
@@ -92,25 +96,25 @@
92
96
  <p>Press <kbd>P</kbd> and click elements on the slide. Each click gets a number. Then write what to change, e.g.
93
97
  <em>“1: align with 2, make 3 smaller”</em>.</p>
94
98
  <p>Or edit yourself: click to select, drag to move, pull a handle to resize, arrow keys to nudge,
95
- double-click to change the text. A selected element is what Claude takes “this” to mean.</p>
96
- <p>Add figures, audio, a PDF or a screenshot with <strong>+</strong>, or drag and drop or paste a file; Claude can put it on a slide
99
+ double-click to change the text. A selected element is what the assistant takes “this” to mean.</p>
100
+ <p>Add figures, audio, a PDF or a screenshot with <strong>+</strong>, or drag and drop or paste a file; the assistant can put it on a slide
97
101
  or, for a paper or report, take the details from it.</p>
98
- <p>Claude edits <code>index.html</code> and <code>theme.css</code>, checks the layout, and the slide updates here.
102
+ <p>The assistant edits <code>index.html</code> and <code>theme.css</code>, checks the layout, and the slide updates here.
99
103
  <code>brand.css</code> is locked.</p>
100
104
  </div>
101
105
  </div>
102
106
  <div id="drop-hint" class="drop-hint" hidden><div>Drop figures, audio or PDFs to attach them</div></div>
103
- <div id="working" class="working" hidden><span class="spinner"></span> <span id="working-text">Claude is working…</span></div>
107
+ <div id="working" class="working" hidden><span class="spinner"></span> <span id="working-text">The assistant is working…</span></div>
104
108
  <div class="composer">
105
109
  <div id="pin-tray" class="pin-tray"></div>
106
110
  <div id="attach-tray" class="attach-tray"></div>
107
- <textarea id="input" rows="3" placeholder="Ask Claude… refer to pins by number (1, 2, …)"></textarea>
111
+ <textarea id="input" rows="3" placeholder="Ask for a change… refer to pins by number (1, 2, …)"></textarea>
108
112
  <div class="composer-row">
109
113
  <span class="menu-wrap">
110
114
  <button id="add-btn" class="add-round" aria-haspopup="menu" aria-expanded="false" aria-label="Add to the message"
111
115
  title="Add a file or a screenshot to the message">+</button>
112
116
  <div id="add-menu" class="menu up" role="menu" hidden>
113
- <button role="menuitem" data-add="file"><strong>File…</strong><span>A figure, audio file or PDF (a figure, table, paper or report) for Claude to use (or drop or paste it)</span></button>
117
+ <button role="menuitem" data-add="file"><strong>File…</strong><span>A figure, audio file or PDF (a figure, table, paper or report) for the assistant to use (or drop or paste it)</span></button>
114
118
  <button role="menuitem" data-add="shot" id="add-shot"><strong>Screenshot</strong><span>This slide with its pins, for questions about how it looks</span></button>
115
119
  </div>
116
120
  </span>
@@ -124,6 +128,19 @@
124
128
  </aside>
125
129
  </div>
126
130
 
131
+ <dialog id="provider-switch" class="delete-dialog">
132
+ <form method="dialog">
133
+ <h3 id="provider-switch-title">Switch provider?</h3>
134
+ <p>This starts a new conversation. Carry over a summary of the current conversation?</p>
135
+ <p class="muted">The summary shares recent messages with the selected provider. Your slides and undo history stay unchanged.</p>
136
+ <div class="dialog-buttons">
137
+ <button value="cancel" autofocus>Cancel</button>
138
+ <button value="fresh">Start fresh</button>
139
+ <button value="summary" class="primary">Switch with summary</button>
140
+ </div>
141
+ </form>
142
+ </dialog>
143
+
127
144
  <dialog id="delete-dialog" class="delete-dialog">
128
145
  <form method="dialog">
129
146
  <h3 id="delete-title">Delete this element?</h3>
@@ -146,7 +163,7 @@
146
163
  <div class="dialog-buttons">
147
164
  <span class="import-notes">
148
165
  <label class="check" id="import-refine-wrap" hidden><input type="checkbox" id="import-refine" checked>
149
- <span id="import-refine-label">Then ask Claude to improve the complex slides</span></label>
166
+ <span id="import-refine-label">Then ask the assistant to improve the complex slides</span></label>
150
167
  <span class="muted small-note">Added after the current slide. You can undo this with ⌘Z.</span>
151
168
  </span>
152
169
  <button value="cancel">Cancel</button>