pinokiod 3.181.0 → 3.182.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 (46) hide show
  1. package/kernel/util.js +15 -2
  2. package/package.json +1 -1
  3. package/server/index.js +111 -12
  4. package/server/public/common.js +433 -240
  5. package/server/public/files-app/app.css +64 -0
  6. package/server/public/files-app/app.js +87 -0
  7. package/server/public/install.js +8 -1
  8. package/server/public/layout.js +11 -1
  9. package/server/public/sound/beep.mp3 +0 -0
  10. package/server/public/sound/bell.mp3 +0 -0
  11. package/server/public/sound/bright-ring.mp3 +0 -0
  12. package/server/public/sound/clap.mp3 +0 -0
  13. package/server/public/sound/deep-ring.mp3 +0 -0
  14. package/server/public/sound/gasp.mp3 +0 -0
  15. package/server/public/sound/hehe.mp3 +0 -0
  16. package/server/public/sound/levelup.mp3 +0 -0
  17. package/server/public/sound/light-pop.mp3 +0 -0
  18. package/server/public/sound/light-ring.mp3 +0 -0
  19. package/server/public/sound/meow.mp3 +0 -0
  20. package/server/public/sound/piano.mp3 +0 -0
  21. package/server/public/sound/pop.mp3 +0 -0
  22. package/server/public/sound/uhoh.mp3 +0 -0
  23. package/server/public/sound/whistle.mp3 +0 -0
  24. package/server/public/style.css +173 -2
  25. package/server/public/tab-idle-notifier.js +697 -4
  26. package/server/public/terminal-settings.js +1131 -0
  27. package/server/public/urldropdown.css +28 -1
  28. package/server/views/{terminals.ejs → agents.ejs} +97 -30
  29. package/server/views/app.ejs +112 -65
  30. package/server/views/bootstrap.ejs +8 -0
  31. package/server/views/connect.ejs +1 -1
  32. package/server/views/d.ejs +172 -18
  33. package/server/views/editor.ejs +8 -0
  34. package/server/views/file_browser.ejs +4 -0
  35. package/server/views/index.ejs +1 -1
  36. package/server/views/init/index.ejs +9 -1
  37. package/server/views/install.ejs +8 -0
  38. package/server/views/net.ejs +1 -1
  39. package/server/views/network.ejs +1 -1
  40. package/server/views/pro.ejs +8 -0
  41. package/server/views/prototype/index.ejs +8 -0
  42. package/server/views/screenshots.ejs +1 -2
  43. package/server/views/settings.ejs +1 -2
  44. package/server/views/shell.ejs +8 -0
  45. package/server/views/terminal.ejs +8 -0
  46. package/server/views/tools.ejs +1 -2
@@ -415,9 +415,30 @@ body.dark .create-launcher-modal-tools-title {
415
415
  color: rgba(203, 213, 225, 0.7);
416
416
  }
417
417
  .create-launcher-modal-tools-options {
418
+ display: grid;
419
+ grid-template-columns: repeat(2, minmax(0, 1fr));
420
+ gap: 12px;
421
+ align-items: flex-start;
422
+ }
423
+ .create-launcher-modal-tools-group {
418
424
  display: flex;
425
+ flex-direction: column;
426
+ gap: 8px;
427
+ }
428
+ .create-launcher-modal-tools-group-title {
429
+ font-size: 11px;
430
+ font-weight: 600;
431
+ letter-spacing: 0.08em;
432
+ text-transform: uppercase;
433
+ color: rgba(71, 85, 105, 0.75);
434
+ }
435
+ body.dark .create-launcher-modal-tools-group-title {
436
+ color: rgba(203, 213, 225, 0.65);
437
+ }
438
+ .create-launcher-modal-tools-group-options {
439
+ display: flex;
440
+ flex-direction: column;
419
441
  gap: 10px;
420
- flex-wrap: wrap;
421
442
  }
422
443
  .create-launcher-modal-tool {
423
444
  position: relative;
@@ -462,6 +483,12 @@ body.dark .create-launcher-modal-tool.selected {
462
483
  background: rgba(127, 91, 243, 0.28);
463
484
  box-shadow: 0 14px 40px rgba(5, 9, 25, 0.55);
464
485
  }
486
+
487
+ @media (max-width: 720px) {
488
+ .create-launcher-modal-tools-options {
489
+ grid-template-columns: 1fr;
490
+ }
491
+ }
465
492
  .create-launcher-modal-tool input[type="radio"] {
466
493
  position: absolute;
467
494
  opacity: 0;
@@ -1,6 +1,6 @@
1
1
  <html>
2
2
  <head>
3
- <title>Pinokio Terminals</title>
3
+ <title>Pinokio Agents</title>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
6
6
  <link href="/xterm.min.css" rel="stylesheet" />
@@ -53,7 +53,6 @@ body.plugin-page .btn-tab {
53
53
  display: flex;
54
54
  align-items: center;
55
55
  gap: 10px;
56
- margin-bottom: 15px;
57
56
  }
58
57
  body.plugin-page .btn-tab .btn {
59
58
  display: flex;
@@ -76,6 +75,32 @@ body.plugin-page .btn-tab .btn {
76
75
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
77
76
  gap: 14px;
78
77
  }
78
+ .plugin-category-layout {
79
+ display: grid;
80
+ grid-template-columns: repeat(2, minmax(0, 1fr));
81
+ gap: 24px;
82
+ }
83
+ .plugin-category {
84
+ display: flex;
85
+ flex-direction: column;
86
+ }
87
+ .plugin-category-header {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 4px;
91
+ margin-bottom: 12px;
92
+ }
93
+ .plugin-category-title {
94
+ font-size: 30px;
95
+ font-weight: 700;
96
+ margin: 0;
97
+ }
98
+ .plugin-category-subtitle {
99
+ font-size: 12px;
100
+ opacity: 0.6;
101
+ text-transform: uppercase;
102
+ letter-spacing: 0.08em;
103
+ }
79
104
  .plugin-card {
80
105
  display: flex;
81
106
  align-items: center;
@@ -195,6 +220,9 @@ body.dark .plugin-card:focus-visible .disclosure-indicator {
195
220
  background: rgba(0, 0, 0, 0.02);
196
221
  text-align: center;
197
222
  }
223
+ .plugin-empty.plugin-empty-category {
224
+ padding: 20px;
225
+ }
198
226
  body.dark .plugin-empty {
199
227
  border-color: rgba(255, 255, 255, 0.16);
200
228
  background: rgba(255, 255, 255, 0.04);
@@ -305,6 +333,11 @@ body.dark .plugin-option:hover {
305
333
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
306
334
  }
307
335
  }
336
+ @media (max-width: 900px) {
337
+ .plugin-category-layout {
338
+ grid-template-columns: 1fr;
339
+ }
340
+ }
308
341
  @media (max-width: 600px) {
309
342
  body.plugin-page main {
310
343
  flex-direction: column;
@@ -394,37 +427,71 @@ body.dark .plugin-option:hover {
394
427
  </header>
395
428
  <main class='plugin-main'>
396
429
  <div class='plugin-container'>
397
- <h1><i class="fa-solid fa-desktop"></i>Terminals</h1>
430
+ <h1><i class="fa-solid fa-robot"></i>Agents</h1>
431
+ <% const pluginCategories = pluginMenu.reduce((acc, pluginItem, index) => {
432
+ const runs = Array.isArray(pluginItem.run) ? pluginItem.run : []
433
+ const hasExec = runs.some((step) => step && step.method === 'exec')
434
+ const hasShellRun = runs.some((step) => step && step.method === 'shell.run')
435
+ if (hasExec) {
436
+ acc.ide.push({ pluginItem, index })
437
+ } else if (hasShellRun) {
438
+ acc.cli.push({ pluginItem, index })
439
+ } else {
440
+ acc.cli.push({ pluginItem, index })
441
+ }
442
+ return acc
443
+ }, { ide: [], cli: [] }) %>
398
444
  <% if (pluginMenu.length === 0) { %>
399
445
  <div class='plugin-empty'>
400
- <p>No terminals found. Clone or add terminal definitions under <code>~/pinokio/plugin</code> to see them listed here.</p>
401
- <a class='btn' href="/home?mode=download"><i class="fa-solid fa-download"></i> Download Terminals</a>
446
+ <p>No agents found. Clone or add agent definitions under <code>~/pinokio/plugin</code> to see them listed here.</p>
447
+ <a class='btn' href="/home?mode=download"><i class="fa-solid fa-download"></i> Download Agents</a>
402
448
  </div>
403
449
  <% } else { %>
404
- <div class='plugin-grid'>
405
- <% pluginMenu.forEach((pluginItem, index) => { %>
406
- <div class='plugin-card' role="button" tabindex="0" data-plugin-index="<%=index%>">
407
- <% if (pluginItem.image) { %>
408
- <img src="<%=pluginItem.image%>" alt="<%=pluginItem.title || pluginItem.text || 'Plugin'%> icon">
409
- <% } else if (pluginItem.icon) { %>
410
- <div class='plugin-icon'><i class="<%=pluginItem.icon%>"></i></div>
411
- <% } else { %>
412
- <div class='plugin-icon'><i class="fa-solid fa-desktop"></i></div>
413
- <% } %>
414
- <div class='plugin-details'>
415
- <h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
416
- <% if (pluginItem.description) { %>
417
- <div class='subtitle'><%=pluginItem.description%></div>
418
- <% } %>
450
+ <div class='plugin-category-layout'>
451
+ <% [
452
+ { key: 'ide', title: 'IDE', subtitle: 'Launch externally' },
453
+ { key: 'cli', title: 'CLI', subtitle: 'Launch in Pinokio' }
454
+ ].forEach(({ key, title, subtitle }) => {
455
+ const items = pluginCategories[key] || []
456
+ %>
457
+ <section class='plugin-category' aria-label="<%=title%> agents">
458
+ <div class='plugin-category-header'>
459
+ <h2 class='plugin-category-title'><%=title%></h2>
460
+ <div class='plugin-category-subtitle'><%=subtitle%></div>
419
461
  </div>
420
- <div class='flexible'></div>
421
- <% if (pluginItem.link) { %>
422
- <button type='button' class='plugin-info' data-link="<%=pluginItem.link.replace(/"/g, '&quot;')%>" aria-label="Open info"><i class="fa-solid fa-circle-info"></i></button>
462
+ <% if (items.length) { %>
463
+ <div class='plugin-grid'>
464
+ <% items.forEach(({ pluginItem, index }) => { %>
465
+ <div class='plugin-card' role="button" tabindex="0" data-plugin-index="<%=index%>">
466
+ <% if (pluginItem.image) { %>
467
+ <img src="<%=pluginItem.image%>" alt="<%=pluginItem.title || pluginItem.text || 'Plugin'%> icon">
468
+ <% } else if (pluginItem.icon) { %>
469
+ <div class='plugin-icon'><i class="<%=pluginItem.icon%>"></i></div>
470
+ <% } else { %>
471
+ <div class='plugin-icon'><i class="fa-solid fa-robot"></i></div>
472
+ <% } %>
473
+ <div class='plugin-details'>
474
+ <h2><%=pluginItem.title || pluginItem.text || pluginItem.name || 'Plugin'%></h2>
475
+ <% if (pluginItem.description) { %>
476
+ <div class='subtitle'><%=pluginItem.description%></div>
477
+ <% } %>
478
+ </div>
479
+ <div class='flexible'></div>
480
+ <% if (pluginItem.link) { %>
481
+ <button type='button' class='plugin-info' data-link="<%=pluginItem.link.replace(/"/g, '&quot;')%>" aria-label="Open info"><i class="fa-solid fa-circle-info"></i></button>
482
+ <% } %>
483
+ <div class='disclosure-indicator' aria-hidden="true">
484
+ <i class="fa-solid fa-chevron-right"></i>
485
+ </div>
486
+ </div>
487
+ <% }) %>
488
+ </div>
489
+ <% } else { %>
490
+ <div class='plugin-empty plugin-empty-category'>
491
+ <p>No <%=title%> agents available.</p>
492
+ </div>
423
493
  <% } %>
424
- <div class='disclosure-indicator' aria-hidden="true">
425
- <i class="fa-solid fa-chevron-right"></i>
426
- </div>
427
- </div>
494
+ </section>
428
495
  <% }) %>
429
496
  </div>
430
497
  <% } %>
@@ -448,7 +515,7 @@ body.dark .plugin-option:hover {
448
515
  <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><i class="fa-solid fa-download"></i><div class='caption'>Download logs</div></a>
449
516
  <a class='tab' href="/screenshots"><i class="fa-solid fa-camera"></i><div class='caption'>Screenshots</div></a>
450
517
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
451
- <a class='tab selected' href="/terminals"><i class="fa-solid fa-desktop"></i><div class='caption'>Terminals</div></a>
518
+ <a class='tab selected' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
452
519
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
453
520
  <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
454
521
  <div class='qr' style='padding:12px 10px; text-align:center;'>
@@ -504,7 +571,7 @@ body.dark .plugin-option:hover {
504
571
  <div class="url-modal-content" role="dialog" aria-modal="true" aria-labelledby="plugin-modal-title" aria-describedby="plugin-modal-description">
505
572
  <button type="button" class="url-modal-close" aria-label="Close">&times;</button>
506
573
  <h3 id="plugin-modal-title">Run Plugin</h3>
507
- <p class="url-modal-description" id="plugin-modal-description">Choose a project to open the terminal from.</p>
574
+ <p class="url-modal-description" id="plugin-modal-description">Choose a project to open the agent from.</p>
508
575
  <input type="search" class="url-modal-input" placeholder="Filter projects" autocomplete="off" />
509
576
  <div class="url-dropdown plugin-modal-dropdown"></div>
510
577
  <div class="url-modal-actions single">
@@ -739,7 +806,7 @@ body.dark .plugin-option:hover {
739
806
  if (state.apps.length === 0) {
740
807
  descriptionEl.textContent = 'No projects found under ~/pinokio/api. Create or download a project to continue.'
741
808
  } else {
742
- descriptionEl.textContent = 'Choose a project to open the terminal from.'
809
+ descriptionEl.textContent = 'Choose a project to open the agent from.'
743
810
  }
744
811
  renderList('')
745
812
  dropdownEl.style.display = 'block'
@@ -891,6 +891,14 @@ body.dark .tab-link-popover {
891
891
  body.dark .tab-link-popover .tab-link-popover-header {
892
892
  color: rgba(226, 232, 240, 0.7);
893
893
  }
894
+ .tab-link-popover .tab-link-popover-separator {
895
+ height: 1px;
896
+ margin: 4px 14px;
897
+ background: rgba(15, 23, 42, 0.08);
898
+ }
899
+ body.dark .tab-link-popover .tab-link-popover-separator {
900
+ background: rgba(148, 163, 184, 0.18);
901
+ }
894
902
  .tab-link-popover .tab-link-popover-item {
895
903
  width: 100%;
896
904
  border: none;
@@ -924,6 +932,10 @@ body.dark .tab-link-popover .tab-link-popover-item:focus-visible {
924
932
  text-transform: uppercase;
925
933
  color: rgba(15, 23, 42, 0.55);
926
934
  }
935
+ .tab-link-popover .tab-link-popover-item .label i {
936
+ margin-right: 6px;
937
+ font-size: 11px;
938
+ }
927
939
  body.dark .tab-link-popover .tab-link-popover-item .label {
928
940
  color: rgba(226, 232, 240, 0.65);
929
941
  }
@@ -4388,27 +4400,27 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
4388
4400
  positionTabLinkPopover(pop, link)
4389
4401
  } catch (_) {}
4390
4402
 
4391
- let entries
4403
+ let entries = []
4392
4404
  try {
4393
- entries = await buildTabLinkEntries(link)
4405
+ const result = await buildTabLinkEntries(link)
4406
+ if (Array.isArray(result)) {
4407
+ entries = result.slice()
4408
+ }
4394
4409
  } catch (_) {
4395
- tabLinkPendingLink = null
4396
- return
4410
+ entries = []
4397
4411
  }
4398
4412
 
4399
4413
  if (tabLinkPendingLink !== link) {
4400
4414
  return
4401
4415
  }
4402
4416
 
4403
- if (!entries || entries.length === 0) {
4404
- hideTabLinkPopover({ immediate: true })
4405
- return
4406
- }
4417
+ let openEntries = Array.isArray(entries) ? entries.slice() : []
4418
+ let showOpenSection = openEntries.length > 0
4407
4419
 
4408
4420
  if (sameOrigin) {
4409
4421
  const slug = extractProjectSlug(link).toLowerCase()
4410
4422
  if (slug) {
4411
- entries = entries.filter((entry) => {
4423
+ openEntries = openEntries.filter((entry) => {
4412
4424
  if (!entry || !entry.url) {
4413
4425
  return false
4414
4426
  }
@@ -4443,17 +4455,21 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
4443
4455
  return false
4444
4456
  })
4445
4457
  } else {
4446
- entries = entries.filter((entry) => entry.url === canonicalBase)
4458
+ openEntries = openEntries.filter((entry) => entry.url === canonicalBase)
4447
4459
  }
4448
4460
 
4449
- const hasAlternate = entries.some((entry) => entry.url !== canonicalBase)
4461
+ const hasAlternate = openEntries.some((entry) => entry.url !== canonicalBase)
4450
4462
  if (!hasAlternate) {
4451
- hideTabLinkPopover({ immediate: true })
4452
- return
4463
+ showOpenSection = false
4464
+ openEntries = []
4465
+ } else {
4466
+ showOpenSection = openEntries.length > 0
4453
4467
  }
4468
+ } else {
4469
+ showOpenSection = openEntries.length > 0
4454
4470
  }
4455
4471
 
4456
- if (!entries || entries.length === 0) {
4472
+ if (!showOpenSection && !canonicalBase) {
4457
4473
  hideTabLinkPopover({ immediate: true })
4458
4474
  return
4459
4475
  }
@@ -4461,62 +4477,93 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
4461
4477
  const popover = ensureTabLinkPopoverEl()
4462
4478
  popover.innerHTML = ""
4463
4479
 
4464
- const header = document.createElement("div")
4465
- header.className = "tab-link-popover-header"
4466
- header.innerHTML = `<i class="fa-solid fa-arrow-up-right-from-square"></i><span>Open in browser</span>`
4467
- popover.appendChild(header)
4468
-
4469
- const hasHttpsEntry = entries.some((entry) => entry && entry.type === "https")
4470
-
4471
- entries.forEach((entry) => {
4472
- const item = document.createElement("button")
4473
- item.type = "button"
4474
- item.setAttribute("data-url", entry.url)
4475
- const labelSpan = document.createElement("span")
4476
- labelSpan.className = "label"
4477
- labelSpan.textContent = entry.label
4478
- const valueSpan = document.createElement("span")
4479
- valueSpan.className = "value"
4480
- valueSpan.textContent = entry.display
4481
-
4482
- if (entry.type === 'http' && entry.qr === true) {
4483
- item.className = "tab-link-popover-item qr-inline"
4484
- const textCol = document.createElement('div')
4485
- textCol.className = 'textcol'
4486
- textCol.append(labelSpan, valueSpan)
4487
- const qrImg = document.createElement('img')
4488
- qrImg.className = 'qr'
4489
- qrImg.alt = 'QR'
4490
- qrImg.decoding = 'async'
4491
- qrImg.loading = 'lazy'
4492
- qrImg.src = `/qr?data=${encodeURIComponent(entry.url)}&s=4&m=0`
4493
- item.append(textCol, qrImg)
4494
- } else {
4495
- item.className = "tab-link-popover-item"
4496
- // Keep label and value as direct children so column layout applies
4497
- item.append(labelSpan, valueSpan)
4480
+ if (showOpenSection) {
4481
+ const header = document.createElement("div")
4482
+ header.className = "tab-link-popover-header"
4483
+ header.innerHTML = `<i class="fa-solid fa-arrow-up-right-from-square"></i><span>Open in browser</span>`
4484
+ popover.appendChild(header)
4485
+
4486
+ const hasHttpsEntry = openEntries.some((entry) => entry && entry.type === "https")
4487
+
4488
+ openEntries.forEach((entry) => {
4489
+ const item = document.createElement("button")
4490
+ item.type = "button"
4491
+ item.setAttribute("data-url", entry.url)
4492
+ const labelSpan = document.createElement("span")
4493
+ labelSpan.className = "label"
4494
+ labelSpan.textContent = entry.label
4495
+ const valueSpan = document.createElement("span")
4496
+ valueSpan.className = "value"
4497
+ valueSpan.textContent = entry.display
4498
+
4499
+ if (entry.type === 'http' && entry.qr === true) {
4500
+ item.className = "tab-link-popover-item qr-inline"
4501
+ const textCol = document.createElement('div')
4502
+ textCol.className = 'textcol'
4503
+ textCol.append(labelSpan, valueSpan)
4504
+ const qrImg = document.createElement('img')
4505
+ qrImg.className = 'qr'
4506
+ qrImg.alt = 'QR'
4507
+ qrImg.decoding = 'async'
4508
+ qrImg.loading = 'lazy'
4509
+ qrImg.src = `/qr?data=${encodeURIComponent(entry.url)}&s=4&m=0`
4510
+ item.append(textCol, qrImg)
4511
+ } else {
4512
+ item.className = "tab-link-popover-item"
4513
+ // Keep label and value as direct children so column layout applies
4514
+ item.append(labelSpan, valueSpan)
4515
+ }
4516
+ popover.appendChild(item)
4517
+ })
4518
+
4519
+ if (tabLinkRouterHttpsActive === false && !hasHttpsEntry) {
4520
+ const footerButton = document.createElement("button")
4521
+ footerButton.type = "button"
4522
+ footerButton.className = "tab-link-popover-item tab-link-popover-footer"
4523
+ footerButton.setAttribute("data-url", "/network")
4524
+ footerButton.setAttribute("data-target", "_self")
4525
+ footerButton.setAttribute("aria-label", "Open network settings to configure local HTTPS")
4526
+
4527
+ const footerLabel = document.createElement("span")
4528
+ footerLabel.className = "label"
4529
+ footerLabel.textContent = "Custom domain not active"
4530
+
4531
+ const footerValue = document.createElement("span")
4532
+ footerValue.className = "value"
4533
+ footerValue.textContent = "Click to activate"
4534
+
4535
+ footerButton.append(footerLabel, footerValue)
4536
+ popover.appendChild(footerButton)
4498
4537
  }
4499
- popover.appendChild(item)
4500
- })
4538
+ }
4539
+
4540
+ if (showOpenSection && canonicalBase) {
4541
+ const separator = document.createElement("div")
4542
+ separator.className = "tab-link-popover-separator"
4543
+ popover.appendChild(separator)
4544
+ }
4545
+
4546
+ if (canonicalBase) {
4547
+ const debugHeader = document.createElement("div")
4548
+ debugHeader.className = "tab-link-popover-header"
4549
+ debugHeader.innerHTML = `<i class="fa-solid fa-cube"></i><span>Debug</span>`
4550
+ popover.appendChild(debugHeader)
4501
4551
 
4502
- if (tabLinkRouterHttpsActive === false && !hasHttpsEntry) {
4503
- const footerButton = document.createElement("button")
4504
- footerButton.type = "button"
4505
- footerButton.className = "tab-link-popover-item tab-link-popover-footer"
4506
- footerButton.setAttribute("data-url", "/network")
4507
- footerButton.setAttribute("data-target", "_self")
4508
- footerButton.setAttribute("aria-label", "Open network settings to configure local HTTPS")
4552
+ const debugItem = document.createElement("button")
4553
+ debugItem.type = "button"
4554
+ debugItem.className = "tab-link-popover-item"
4555
+ debugItem.setAttribute("data-url", canonicalBase)
4509
4556
 
4510
- const footerLabel = document.createElement("span")
4511
- footerLabel.className = "label"
4512
- footerLabel.textContent = "Custom domain not active"
4557
+ const debugLabel = document.createElement("span")
4558
+ debugLabel.className = "label"
4559
+ debugLabel.innerHTML = `<i class="fa-solid fa-border-none"></i><span>Select a region</span>`
4513
4560
 
4514
- const footerValue = document.createElement("span")
4515
- footerValue.className = "value"
4516
- footerValue.textContent = "Click to activate"
4561
+ const debugValue = document.createElement("span")
4562
+ debugValue.className = "value"
4563
+ debugValue.textContent = formatDisplayUrl(canonicalBase)
4517
4564
 
4518
- footerButton.append(footerLabel, footerValue)
4519
- popover.appendChild(footerButton)
4565
+ debugItem.append(debugLabel, debugValue)
4566
+ popover.appendChild(debugItem)
4520
4567
  }
4521
4568
 
4522
4569
  tabLinkActiveLink = link
@@ -86,6 +86,7 @@ body {
86
86
  <script src="/xterm-theme.js"></script>
87
87
  <script src="/sweetalert2.js"></script>
88
88
  <script src="/Socket.js"></script>
89
+ <script src="/terminal-settings.js"></script>
89
90
  <script>
90
91
  function isASCII(str) {
91
92
  for (let i = 0; i < str.length; i++) {
@@ -145,7 +146,14 @@ document.addEventListener("DOMContentLoaded", () => {
145
146
  if (res && res.config) {
146
147
  config = res.config
147
148
  }
149
+ const baseConfig = Object.assign({}, config)
150
+ if (window.PinokioTerminalSettings && typeof window.PinokioTerminalSettings.applyToConfig === 'function') {
151
+ config = window.PinokioTerminalSettings.applyToConfig(config)
152
+ }
148
153
  const term = new Terminal(config)
154
+ if (window.PinokioTerminalSettings && typeof window.PinokioTerminalSettings.register === 'function') {
155
+ window.PinokioTerminalSettings.register(term, { baseConfig })
156
+ }
149
157
  const fitAddon = new FitAddon.FitAddon();
150
158
  term.loadAddon(fitAddon);
151
159
  term.loadAddon(new WebLinksAddon.WebLinksAddon());
@@ -922,7 +922,7 @@ document.addEventListener('DOMContentLoaded', function() {
922
922
  <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><i class="fa-solid fa-download"></i><div class='caption'>Download logs</div></a>
923
923
  <a class='tab' href="/screenshots"><i class="fa-solid fa-camera"></i><div class='caption'>Screenshots</div></a>
924
924
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
925
- <a class='tab' href="/terminals"><i class="fa-solid fa-desktop"></i><div class='caption'>Terminals</div></a>
925
+ <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
926
926
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
927
927
  <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
928
928
  <div class='qr' style='padding:12px 10px; text-align:center;'>