vibespot 1.3.1 → 1.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibespot",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "AI-powered HubSpot CMS landing page builder — vibe coding & React converter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,8 @@
19
19
  "build": "tsup",
20
20
  "test": "vitest run",
21
21
  "test:watch": "vitest",
22
- "prepublishOnly": "npm run build"
22
+ "prepublishOnly": "npm run build",
23
+ "contact-monitor": "tsx scripts/contact-monitor.ts"
23
24
  },
24
25
  "dependencies": {
25
26
  "@anthropic-ai/sdk": "^0.92.0",
package/ui/dashboard.js CHANGED
@@ -1292,15 +1292,16 @@ if (humanifyCheckbox) {
1292
1292
  // ---------------------------------------------------------------------------
1293
1293
 
1294
1294
  function switchWorkspaceTab(tabName) {
1295
+ if (tabName === "settings") {
1296
+ if (typeof openSettings === "function") openSettings();
1297
+ return;
1298
+ }
1295
1299
  document.querySelectorAll(".workspace-tab").forEach((btn) => {
1296
1300
  btn.classList.toggle("active", btn.dataset.wsTab === tabName);
1297
1301
  });
1298
1302
  document.querySelectorAll(".workspace-panel").forEach((panel) => {
1299
1303
  panel.classList.toggle("active", panel.dataset.wsPanel === tabName);
1300
1304
  });
1301
- if (tabName === "settings" && typeof refreshSettings === "function") {
1302
- refreshSettings();
1303
- }
1304
1305
  if (tabName === "library") {
1305
1306
  refreshDashboard();
1306
1307
  }
@@ -112,6 +112,12 @@
112
112
  <a class="doc-nav__link doc-nav__link--sub" href="#auto-fix">Auto-Fix</a>
113
113
  <a class="doc-nav__link doc-nav__link--sub" href="#creating-page-hubspot">Creating a Page</a>
114
114
  <a class="doc-nav__link" href="#zip-download">ZIP Download</a>
115
+ <a class="doc-nav__link" href="#docker-deployment">Docker Deployment</a>
116
+ <a class="doc-nav__link doc-nav__link--sub" href="#docker-quick-start">Quick Start</a>
117
+ <a class="doc-nav__link doc-nav__link--sub" href="#docker-https">HTTPS with Caddy</a>
118
+ <a class="doc-nav__link doc-nav__link--sub" href="#docker-env-vars">Environment Variables</a>
119
+ <a class="doc-nav__link doc-nav__link--sub" href="#docker-persistence">Persistence</a>
120
+ <a class="doc-nav__link doc-nav__link--sub" href="#docker-reverse-proxy">Reverse Proxy &amp; k8s</a>
115
121
  <a class="doc-nav__link" href="#marketplace">Marketplace</a>
116
122
  <a class="doc-nav__link" href="#version-history">Version History</a>
117
123
  </div>
@@ -195,7 +201,7 @@ vibespot</code></pre>
195
201
  <div class="doc-feature">
196
202
  <div class="doc-feature__icon">&#127912;</div>
197
203
  <div class="doc-feature__title">Multi-Engine AI</div>
198
- <div class="doc-feature__desc">Works with Anthropic, OpenAI, Gemini, Claude Code, Gemini CLI, and Codex CLI.</div>
204
+ <div class="doc-feature__desc">Works with Anthropic, OpenAI, Gemini, Langdock, Claude Code, Gemini CLI, and Codex CLI.</div>
199
205
  </div>
200
206
  <div class="doc-feature">
201
207
  <div class="doc-feature__icon">&#128338;</div>
@@ -213,7 +219,7 @@ vibespot</code></pre>
213
219
  <p>vibeSpot stores its configuration at <code>~/.vibespot/config.json</code>. You can configure everything through the Settings panel in the web UI, or by editing the config file directly.</p>
214
220
 
215
221
  <h3 id="ai-engines">AI Engines</h3>
216
- <p>vibeSpot supports seven AI engines. You only need one to get started. The table below compares them:</p>
222
+ <p>vibeSpot supports eight AI engines. You only need one to get started. The table below compares them:</p>
217
223
 
218
224
  <table>
219
225
  <thead>
@@ -225,6 +231,7 @@ vibespot</code></pre>
225
231
  <tr><td>Claude OAuth</td><td>API</td><td>Run <code>claude setup-token</code>, paste token</td><td>Included in Claude subscription</td><td>Subscribers without API budget</td></tr>
226
232
  <tr><td>OpenAI API</td><td>API</td><td>Add <code>OPENAI_API_KEY</code></td><td>Pay-per-token</td><td>GPT-5.5 users, existing OpenAI budget</td></tr>
227
233
  <tr><td>Gemini API</td><td>API</td><td>Add <code>GEMINI_API_KEY</code></td><td>Free tier available</td><td>Cost-conscious users, free quota</td></tr>
234
+ <tr><td>Langdock</td><td>API</td><td>Add <code>LANGDOCK_API_KEY</code></td><td>Pay-per-token (via Langdock)</td><td>EU data residency, GDPR compliance</td></tr>
228
235
  <tr><td>Gemini CLI</td><td>CLI</td><td>Install <code>gemini</code> CLI, authenticate</td><td>Free (with Google account)</td><td>Free option without API keys</td></tr>
229
236
  <tr><td>Codex CLI</td><td>CLI</td><td>Install <code>codex</code> CLI, authenticate</td><td>Included in OpenAI subscription</td><td>OpenAI subscribers</td></tr>
230
237
  </tbody>
@@ -237,6 +244,7 @@ vibespot</code></pre>
237
244
  <button class="doc-tabs__tab" data-tab="tab-claude-oauth">Claude OAuth</button>
238
245
  <button class="doc-tabs__tab" data-tab="tab-openai">OpenAI API</button>
239
246
  <button class="doc-tabs__tab" data-tab="tab-gemini-api">Gemini API</button>
247
+ <button class="doc-tabs__tab" data-tab="tab-langdock">Langdock</button>
240
248
  <button class="doc-tabs__tab" data-tab="tab-gemini-cli">Gemini CLI</button>
241
249
  <button class="doc-tabs__tab" data-tab="tab-codex">Codex CLI</button>
242
250
  </div>
@@ -284,6 +292,18 @@ vibespot</code></pre>
284
292
  <li>Select <strong>Gemini API</strong> as the engine.</li>
285
293
  </ol>
286
294
  </div>
295
+ <div class="doc-tabs__panel" id="tab-langdock">
296
+ <p>Langdock is an EU-hosted AI gateway based in Frankfurt. It routes Claude requests through an Anthropic-compatible endpoint, so all Anthropic features (prompt caching, structured output, extended thinking) work unchanged. Langdock provides a single GDPR-native DPA covering OpenAI, Anthropic, Mistral, and Google models.</p>
297
+ <ol class="doc-steps">
298
+ <li>Sign up at <a href="https://langdock.com" target="_blank">langdock.com</a> and generate an API key.</li>
299
+ <li>In vibeSpot Settings &rarr; AI tab, paste the key into the Langdock API Key field. Or set the environment variable: <code>export LANGDOCK_API_KEY=...</code></li>
300
+ <li>Select <strong>Langdock</strong> as the engine. The default model is <code>claude-sonnet-4-20250514</code>. All Claude models are available through the gateway.</li>
301
+ </ol>
302
+ <div class="doc-callout doc-callout--info">
303
+ <div class="doc-callout__label">&#8505; Self-Hosted Deployments</div>
304
+ <p>For private-cloud or self-hosted Langdock installations, set <code>LANGDOCK_BASE_URL</code> to override the default endpoint (<code>https://api.langdock.com/anthropic</code>). You can also set this in the config file as <code>langdockBaseUrl</code>.</p>
305
+ </div>
306
+ </div>
287
307
  <div class="doc-tabs__panel" id="tab-gemini-cli">
288
308
  <p>The Gemini CLI runs as a local subprocess, similar to Claude Code.</p>
289
309
  <ol class="doc-steps">
@@ -325,10 +345,13 @@ vibespot</code></pre>
325
345
  <tr><th>Field</th><th>Type</th><th>Description</th></tr>
326
346
  </thead>
327
347
  <tbody>
328
- <tr><td><code>aiEngine</code></td><td>string</td><td>Active AI engine: <code>"anthropic-api"</code>, <code>"claude-code"</code>, <code>"claude-oauth"</code>, <code>"openai-api"</code>, <code>"gemini-api"</code>, <code>"gemini-cli"</code>, <code>"codex-cli"</code></td></tr>
348
+ <tr><td><code>aiEngine</code></td><td>string</td><td>Active AI engine: <code>"anthropic-api"</code>, <code>"claude-code"</code>, <code>"claude-oauth"</code>, <code>"openai-api"</code>, <code>"gemini-api"</code>, <code>"langdock-api"</code>, <code>"gemini-cli"</code>, <code>"codex-cli"</code></td></tr>
329
349
  <tr><td><code>anthropicApiKey</code></td><td>string</td><td>Anthropic API key (starts with <code>sk-ant-</code>)</td></tr>
330
350
  <tr><td><code>openaiApiKey</code></td><td>string</td><td>OpenAI API key (starts with <code>sk-</code>)</td></tr>
331
351
  <tr><td><code>geminiApiKey</code></td><td>string</td><td>Google AI / Gemini API key</td></tr>
352
+ <tr><td><code>langdockApiKey</code></td><td>string</td><td>Langdock API key for the EU gateway</td></tr>
353
+ <tr><td><code>langdockApiModel</code></td><td>string</td><td>Langdock model override (default: <code>claude-sonnet-4-20250514</code>)</td></tr>
354
+ <tr><td><code>langdockBaseUrl</code></td><td>string</td><td>Override Langdock endpoint for self-hosted/private-cloud deployments (default: <code>https://api.langdock.com/anthropic</code>)</td></tr>
332
355
  <tr><td><code>claudeCodeModel</code></td><td>string</td><td>Model override for Claude Code CLI</td></tr>
333
356
  <tr><td><code>anthropicApiModel</code></td><td>string</td><td>Anthropic API model (default: <code>claude-sonnet-4-20250514</code>)</td></tr>
334
357
  <tr><td><code>openaiApiModel</code></td><td>string</td><td>OpenAI API model (default: <code>gpt-5.5</code>)</td></tr>
@@ -342,7 +365,7 @@ vibespot</code></pre>
342
365
  </table>
343
366
 
344
367
  <h3 id="env-vars">Environment Variables</h3>
345
- <p>API keys can also be set as environment variables. The config file takes precedence &mdash; environment variables are used as fallbacks when no key is set in the config:</p>
368
+ <p>API keys and server settings can be set as environment variables. The config file takes precedence &mdash; environment variables are used as fallbacks when no key is set in the config. In Docker deployments, env vars are the primary configuration method (see <a href="#docker-deployment">Docker Deployment</a>).</p>
346
369
  <table>
347
370
  <thead>
348
371
  <tr><th>Variable</th><th>Description</th></tr>
@@ -352,6 +375,14 @@ vibespot</code></pre>
352
375
  <tr><td><code>OPENAI_API_KEY</code></td><td>OpenAI API key for GPT models</td></tr>
353
376
  <tr><td><code>GEMINI_API_KEY</code></td><td>Google AI API key for Gemini models</td></tr>
354
377
  <tr><td><code>GOOGLE_AI_API_KEY</code></td><td>Alternative name for the Gemini API key (same effect as <code>GEMINI_API_KEY</code>)</td></tr>
378
+ <tr><td><code>LANGDOCK_API_KEY</code></td><td>Langdock EU gateway key</td></tr>
379
+ <tr><td><code>LANGDOCK_BASE_URL</code></td><td>Override Langdock endpoint for self-hosted deployments</td></tr>
380
+ <tr><td><code>HUBSPOT_PERSONAL_ACCESS_KEY</code></td><td>HubSpot PAK &mdash; enables theme upload without configuring through the UI</td></tr>
381
+ <tr><td><code>FIGMA_TOKEN</code></td><td>Figma personal access token for design import</td></tr>
382
+ <tr><td><code>VIBESPOT_AI_ENGINE</code></td><td>Default engine: <code>anthropic-api</code>, <code>openai-api</code>, <code>gemini-api</code>, <code>langdock-api</code></td></tr>
383
+ <tr><td><code>VIBESPOT_AGENTIC_MODE</code></td><td>Set <code>true</code> to enable the multi-stage agentic pipeline</td></tr>
384
+ <tr><td><code>VIBESPOT_PORT</code></td><td>Override the HTTP server port (default: <code>4200</code>)</td></tr>
385
+ <tr><td><code>VIBESPOT_NO_OPEN</code></td><td>Set <code>1</code> to suppress auto-opening the browser on startup</td></tr>
355
386
  </tbody>
356
387
  </table>
357
388
  </div>
@@ -1264,6 +1295,76 @@ testimonial carousel, and a minimal footer with social links.</code></pre>
1264
1295
  </div>
1265
1296
  </div>
1266
1297
 
1298
+ <!-- ============================================================
1299
+ Section: Docker Deployment
1300
+ ============================================================ -->
1301
+ <div class="doc-section" id="docker-deployment">
1302
+ <h2 id="docker-deployment-heading">Docker Deployment <a href="#docker-deployment" class="doc-anchor">#</a></h2>
1303
+ <p>Run vibeSpot as a containerised service for your team. The Docker image bundles the server, UI, and all dependencies &mdash; just add an AI API key.</p>
1304
+
1305
+ <h3 id="docker-quick-start">Quick Start (LAN / VPN) <a href="#docker-quick-start" class="doc-anchor">#</a></h3>
1306
+ <pre><code>cp .env.example .env
1307
+ # Edit .env &mdash; set at least one AI API key (e.g. ANTHROPIC_API_KEY)
1308
+ docker compose up -d</code></pre>
1309
+ <p>Open <code>http://&lt;host-ip&gt;:4200</code> in a browser. All AI keys, engine selection, and HubSpot credentials are configured via the <code>.env</code> file &mdash; no interactive setup needed.</p>
1310
+
1311
+ <h3 id="docker-https">HTTPS with Caddy <a href="#docker-https" class="doc-anchor">#</a></h3>
1312
+ <p>For public or semi-public deployments, activate the Caddy reverse proxy profile. Caddy auto-provisions a TLS certificate from Let&rsquo;s Encrypt.</p>
1313
+ <pre><code>cp .env.example .env
1314
+ # Set VIBESPOT_DOMAIN=vibespot.example.com and your AI key
1315
+ docker compose --profile https up -d</code></pre>
1316
+ <p>Caddy binds ports 80 and 443. Point your DNS A record at the host, and HTTPS works automatically.</p>
1317
+
1318
+ <h3 id="docker-env-vars">Environment Variables <a href="#docker-env-vars" class="doc-anchor">#</a></h3>
1319
+ <p>The Docker image reads all configuration from environment variables. The <code>.env.example</code> file documents every option.</p>
1320
+
1321
+ <table>
1322
+ <thead>
1323
+ <tr><th>Variable</th><th>Default</th><th>Description</th></tr>
1324
+ </thead>
1325
+ <tbody>
1326
+ <tr><td><code>VIBESPOT_PORT</code></td><td><code>4200</code></td><td>Port the server listens on inside the container</td></tr>
1327
+ <tr><td><code>VIBESPOT_DOMAIN</code></td><td>&mdash;</td><td>Public domain for the Caddy HTTPS profile</td></tr>
1328
+ <tr><td><code>VIBESPOT_NO_OPEN</code></td><td><code>1</code></td><td>Suppress auto-open browser (set automatically in Docker)</td></tr>
1329
+ <tr><td><code>VIBESPOT_AI_ENGINE</code></td><td>&mdash;</td><td>Default engine: <code>anthropic-api</code>, <code>openai-api</code>, <code>gemini-api</code>, <code>langdock-api</code></td></tr>
1330
+ <tr><td><code>VIBESPOT_AGENTIC_MODE</code></td><td>&mdash;</td><td>Set <code>true</code> to enable the multi-stage agentic pipeline</td></tr>
1331
+ <tr><td><code>ANTHROPIC_API_KEY</code></td><td>&mdash;</td><td>Anthropic Claude API key</td></tr>
1332
+ <tr><td><code>OPENAI_API_KEY</code></td><td>&mdash;</td><td>OpenAI API key</td></tr>
1333
+ <tr><td><code>GEMINI_API_KEY</code></td><td>&mdash;</td><td>Google Gemini API key</td></tr>
1334
+ <tr><td><code>LANGDOCK_API_KEY</code></td><td>&mdash;</td><td>Langdock EU gateway key</td></tr>
1335
+ <tr><td><code>HUBSPOT_PERSONAL_ACCESS_KEY</code></td><td>&mdash;</td><td>HubSpot PAK for theme uploads</td></tr>
1336
+ <tr><td><code>FIGMA_TOKEN</code></td><td>&mdash;</td><td>Figma personal access token for design import</td></tr>
1337
+ </tbody>
1338
+ </table>
1339
+
1340
+ <h3 id="docker-persistence">Persistence <a href="#docker-persistence" class="doc-anchor">#</a></h3>
1341
+ <p>Two named Docker volumes keep data across container restarts:</p>
1342
+ <table>
1343
+ <thead>
1344
+ <tr><th>Volume</th><th>Container path</th><th>Contents</th></tr>
1345
+ </thead>
1346
+ <tbody>
1347
+ <tr><td><code>vibespot-config</code></td><td><code>/home/vibespot/.vibespot</code></td><td>config.json, session data</td></tr>
1348
+ <tr><td><code>vibespot-themes</code></td><td><code>/home/vibespot/vibespot-themes</code></td><td>Generated HubSpot themes</td></tr>
1349
+ </tbody>
1350
+ </table>
1351
+ <p>To back up:</p>
1352
+ <pre><code>docker compose cp vibespot:/home/vibespot/.vibespot ./backup-config
1353
+ docker compose cp vibespot:/home/vibespot/vibespot-themes ./backup-themes</code></pre>
1354
+
1355
+ <h3 id="docker-reverse-proxy">Reverse Proxy &amp; Kubernetes <a href="#docker-reverse-proxy" class="doc-anchor">#</a></h3>
1356
+ <p>If you already have a reverse proxy (nginx, Traefik, k8s Ingress), skip the Caddy profile and point your proxy at port 4200. Key requirements:</p>
1357
+ <ul>
1358
+ <li><strong>WebSocket upgrade</strong> &mdash; the chat, pipeline progress, and upload UI all use a persistent WebSocket connection.</li>
1359
+ <li><strong>Timeout &gt; 120s</strong> &mdash; agentic AI generation can run for several minutes. Set read/send timeouts to at least 300s.</li>
1360
+ <li><strong>No buffering</strong> &mdash; for streaming AI responses, disable proxy buffering.</li>
1361
+ </ul>
1362
+ <div class="doc-callout doc-callout--tip">
1363
+ <div class="doc-callout__label">&#128214; Full guide</div>
1364
+ <p>For nginx configs, Kubernetes manifests, security considerations, and troubleshooting, see the complete <a href="https://github.com/borismichel/vibespot/blob/main/docs/docker-deployment.md" target="_blank">Docker Deployment Guide</a>.</p>
1365
+ </div>
1366
+ </div>
1367
+
1267
1368
  <!-- ============================================================
1268
1369
  Section: HubSpot Marketplace publication path
1269
1370
  ============================================================ -->
Binary file
package/ui/index.html CHANGED
@@ -909,28 +909,8 @@
909
909
  </div>
910
910
  </section>
911
911
 
912
- <!-- ================ SETTINGS TAB (formerly modal overlay) ================ -->
912
+ <!-- ================ SETTINGS (now uses global overlay) ================ -->
913
913
  <section class="workspace-panel workspace-panel--settings" id="ws-panel-settings" data-ws-panel="settings" role="tabpanel" aria-labelledby="ws-tab-settings">
914
- <div class="dashboard__container">
915
-
916
- <header class="settings__header">
917
- <h2 class="settings__title">Settings</h2>
918
- </header>
919
- <nav class="settings__tabs" id="settings-tabs" role="tablist" aria-label="Settings categories">
920
- <button class="settings__tab active" data-tab="ai" type="button" role="tab" aria-selected="true">AI</button>
921
- <button class="settings__tab" data-tab="hubspot" type="button" role="tab" aria-selected="false">HubSpot</button>
922
- <button class="settings__tab" data-tab="figma" type="button" role="tab" aria-selected="false">Figma</button>
923
- <button class="settings__tab" data-tab="github" type="button" role="tab" aria-selected="false">GitHub</button>
924
- <button class="settings__tab" data-tab="vibespot" type="button" role="tab" aria-selected="false">vibeSpot</button>
925
- </nav>
926
- <div class="settings__body" id="settings-body" role="tabpanel">
927
- <div class="settings__loading">
928
- <div class="settings__spinner-lg" aria-hidden="true"></div>
929
- <span>Loading environment...</span>
930
- </div>
931
- </div>
932
-
933
- </div>
934
914
  </section>
935
915
 
936
916
  </div><!-- /.dashboard__body -->
@@ -981,6 +961,31 @@
981
961
 
982
962
  </div><!-- /.app-body -->
983
963
 
964
+ <!-- ============================================================ -->
965
+ <!-- SETTINGS OVERLAY — global modal, works from any screen -->
966
+ <!-- ============================================================ -->
967
+ <div class="settings-overlay hidden" id="settings-overlay">
968
+ <div class="settings-panel">
969
+ <header class="settings__header">
970
+ <h2 class="settings__title">Settings</h2>
971
+ <button class="settings__close" id="settings-close" type="button" aria-label="Close settings">&times;</button>
972
+ </header>
973
+ <nav class="settings__tabs" id="settings-tabs" role="tablist" aria-label="Settings categories">
974
+ <button class="settings__tab active" data-tab="ai" type="button" role="tab" aria-selected="true">AI</button>
975
+ <button class="settings__tab" data-tab="hubspot" type="button" role="tab" aria-selected="false">HubSpot</button>
976
+ <button class="settings__tab" data-tab="figma" type="button" role="tab" aria-selected="false">Figma</button>
977
+ <button class="settings__tab" data-tab="github" type="button" role="tab" aria-selected="false">GitHub</button>
978
+ <button class="settings__tab" data-tab="vibespot" type="button" role="tab" aria-selected="false">vibeSpot</button>
979
+ </nav>
980
+ <div class="settings__body" id="settings-body" role="tabpanel">
981
+ <div class="settings__loading">
982
+ <div class="settings__spinner-lg" aria-hidden="true"></div>
983
+ <span>Loading environment...</span>
984
+ </div>
985
+ </div>
986
+ </div>
987
+ </div>
988
+
984
989
  <script src="/vendor/marked.umd.js"></script>
985
990
  <script src="/vendor/codemirror-bundle.global.js"></script>
986
991
  <script src="/icons.js"></script>
package/ui/settings.js CHANGED
@@ -18,14 +18,13 @@ const ENGINE_LABELS = {
18
18
  "gemini-cli": "Gemini CLI",
19
19
  "gemini-api": "Gemini API",
20
20
  "codex-cli": "OpenAI Codex",
21
+ "langdock-api": "Langdock",
21
22
  };
22
23
 
23
24
  // ---------------------------------------------------------------------------
24
25
  // Open / Close
25
26
  // ---------------------------------------------------------------------------
26
27
 
27
- let _prevWorkspaceTab = "pages";
28
-
29
28
  function openSettings(tab) {
30
29
  if (typeof closeMenu === "function") closeMenu();
31
30
  if (tab) {
@@ -34,23 +33,13 @@ function openSettings(tab) {
34
33
  tabs.forEach((t) => t.classList.toggle("active", t.dataset.tab === tab));
35
34
  }
36
35
  const overlay = document.getElementById("settings-overlay");
37
- if (overlay) {
38
- overlay.classList.remove("hidden");
39
- } else if (typeof switchWorkspaceTab === "function") {
40
- const activeWs = document.querySelector(".workspace-tab.active");
41
- if (activeWs && activeWs.dataset.wsTab !== "settings") _prevWorkspaceTab = activeWs.dataset.wsTab;
42
- switchWorkspaceTab("settings");
43
- }
36
+ if (overlay) overlay.classList.remove("hidden");
44
37
  refreshSettings();
45
38
  }
46
39
 
47
40
  function closeSettings() {
48
41
  const overlay = document.getElementById("settings-overlay");
49
- if (overlay) {
50
- overlay.classList.add("hidden");
51
- } else if (typeof switchWorkspaceTab === "function") {
52
- switchWorkspaceTab(_prevWorkspaceTab || "pages");
53
- }
42
+ if (overlay) overlay.classList.add("hidden");
54
43
  Object.keys(activePolls).forEach((id) => {
55
44
  clearInterval(activePolls[id]);
56
45
  delete activePolls[id];
@@ -153,6 +142,7 @@ function renderAITab(body, data) {
153
142
  { id: "gemini-cli", label: "Gemini CLI" },
154
143
  { id: "gemini-api", label: "Gemini API" },
155
144
  { id: "codex-cli", label: "Codex CLI" },
145
+ { id: "langdock-api", label: "Langdock" },
156
146
  ];
157
147
 
158
148
  for (const eng of allEngines) {
@@ -271,6 +261,7 @@ function renderAITab(body, data) {
271
261
  { key: "anthropic", name: "Anthropic", placeholder: "sk-ant-api03-..." },
272
262
  { key: "openai", name: "OpenAI", placeholder: "sk-..." },
273
263
  { key: "gemini", name: "Google AI", placeholder: "AIza..." },
264
+ { key: "langdock", name: "Langdock", placeholder: "ld-..." },
274
265
  ];
275
266
 
276
267
  for (const prov of providers) {
@@ -1482,6 +1473,14 @@ function getModelsForEngine(engine) {
1482
1473
  { id: "gpt-5.4-nano", label: "GPT-5.4 Nano" },
1483
1474
  { id: "codex-mini-latest", label: "Codex Mini (latest)" },
1484
1475
  ];
1476
+ case "langdock-api":
1477
+ return [
1478
+ { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (default)" },
1479
+ { id: "claude-opus-4-7", label: "Claude Opus 4.7" },
1480
+ { id: "claude-opus-4-6", label: "Claude Opus 4.6" },
1481
+ { id: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
1482
+ { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
1483
+ ];
1485
1484
  default:
1486
1485
  return [];
1487
1486
  }
@@ -1496,6 +1495,7 @@ function getCurrentModel(engine, config) {
1496
1495
  case "codex-cli": return config.codexCliModel || "gpt-5.5";
1497
1496
  case "gemini-cli": return config.geminiCliModel || "gemini-2.5-pro";
1498
1497
  case "gemini-api": return config.geminiApiModel || "gemini-2.5-pro";
1498
+ case "langdock-api": return config.langdockApiModel || "claude-sonnet-4-6";
1499
1499
  default: return null;
1500
1500
  }
1501
1501
  }
@@ -1558,9 +1558,15 @@ function escSettings(str) {
1558
1558
 
1559
1559
  document.getElementById("btn-setup-settings")?.addEventListener("click", () => openSettings());
1560
1560
 
1561
+ document.getElementById("settings-close")?.addEventListener("click", () => closeSettings());
1562
+
1563
+ document.getElementById("settings-overlay")?.addEventListener("click", (e) => {
1564
+ if (e.target.id === "settings-overlay") closeSettings();
1565
+ });
1566
+
1561
1567
  document.addEventListener("keydown", (e) => {
1562
- const settingsTabActive = document.querySelector('.workspace-tab[data-ws-tab="settings"].active');
1563
- if (e.key === "Escape" && settingsTabActive) {
1568
+ const overlay = document.getElementById("settings-overlay");
1569
+ if (e.key === "Escape" && overlay && !overlay.classList.contains("hidden")) {
1564
1570
  closeSettings();
1565
1571
  }
1566
1572
  });