pinokiod 3.23.0 → 3.25.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.
@@ -55,8 +55,21 @@ img.appicon {
55
55
  margin-bottom: 10px;
56
56
  opacity: 0.7;
57
57
  }
58
+ .explain .well {
59
+ margin-top: 20px;
60
+ background: rgba(0,0,0,0.04);
61
+ /*
62
+ border-left: 5px solid rgba(0,0,0,0.1);
63
+ */
64
+ padding: 20px;
65
+ }
66
+ .explain ol {
67
+ padding-inline-start: 15px;
68
+ }
58
69
  .container {
70
+ /*
59
71
  max-width: 800px;
72
+ */
60
73
  /*
61
74
  margin: 0 auto;
62
75
  padding: 2rem;
@@ -169,9 +182,19 @@ body.dark .tab-nav {
169
182
  }
170
183
 
171
184
  */
185
+ .tab-button h5 {
186
+ font-size: 15px;
187
+ width: 100%;
188
+ }
172
189
  .tab-button {
190
+ /*
173
191
  display: block;
174
- padding: 10px 20px;
192
+ */
193
+ display: flex;
194
+ flex-direction: column;
195
+ width: 220px !important;
196
+ gap: 5px;
197
+ padding: 20px;
175
198
  background: none;
176
199
  border: none;
177
200
  cursor: pointer;
@@ -186,9 +209,17 @@ body.dark .tab-nav {
186
209
  border-bottom: 2px solid rgba(0,0,0,0.1);
187
210
  */
188
211
  }
212
+ .tab-button .col {
213
+ display: block;
214
+ }
215
+ /*
216
+ .tab-button i {
217
+ padding: 5px;
218
+ }
219
+ */
189
220
 
190
221
  body.dark .tab-button {
191
- color: rgba(255,255,255,0.6);
222
+ color: rgba(255,255,255,0.5);
192
223
  }
193
224
 
194
225
  .tab-button:hover {
@@ -213,6 +244,12 @@ body.dark .tab-button:hover {
213
244
 
214
245
  body.dark .tab-button.active {
215
246
  background: rgba(255,255,255,0.06);
247
+ color: white;
248
+ }
249
+
250
+ .row {
251
+ display: flex;
252
+ align-items: center;
216
253
  }
217
254
 
218
255
  .tab-content {
@@ -441,9 +478,6 @@ textarea:focus, input[type="url"]:focus, input[type="text"]:focus {
441
478
 
442
479
 
443
480
 
444
-
445
-
446
-
447
481
  .online {
448
482
  width: 10px;
449
483
  height: 10px;
@@ -499,7 +533,7 @@ body.dark .m {
499
533
  flex-direction: column;
500
534
  flex-grow: 1;
501
535
  margin: 0 auto;
502
- padding: 50px 0;
536
+ padding: 20px;
503
537
  }
504
538
  body aside {
505
539
  /*
@@ -1106,6 +1140,61 @@ body.dark .bubble {
1106
1140
  body.dark .bubble:before {
1107
1141
  border-color: transparent rgba(0,0,0,0.4) transparent transparent;
1108
1142
  }
1143
+ .ai-prompt-section {
1144
+ display: flex;
1145
+ }
1146
+
1147
+ .ai-tabs {
1148
+ display: flex;
1149
+ flex-direction: column;
1150
+ gap: 5px;
1151
+ margin-bottom: 10px;
1152
+ }
1153
+ .ai-tab:hover {
1154
+ color: royalblue;
1155
+ }
1156
+ .ai-tab {
1157
+ cursor: pointer;
1158
+ color: silver;
1159
+ padding: 10px 20px;
1160
+ font-size: 14px;
1161
+ width: 200px;
1162
+ }
1163
+ .ai-tab.selected {
1164
+ color: white;
1165
+ background: rgba(0,0,0,0.1);
1166
+ border-top-left-radius: 20px;
1167
+ border-bottom-left-radius: 20px;
1168
+ font-weight: bold;
1169
+ }
1170
+ .ai-tab-content.selected {
1171
+ display: flex;
1172
+ flex-direction: column;
1173
+ gap: 10px;
1174
+ background: rgba(0,0,0,0.1);
1175
+ }
1176
+ .ai-tab-content .ai-input {
1177
+ background: white !important;
1178
+ color: black !important;
1179
+ }
1180
+ .ai-tab-content {
1181
+ border-top-right-radius: 20px;
1182
+ border-bottom-right-radius: 20px;
1183
+ display: none;
1184
+ padding: 20px;
1185
+ }
1186
+ .ai-container {
1187
+ flex-grow: 1;
1188
+ }
1189
+ .ai-container h1 {
1190
+ padding: 0;
1191
+ font-weight: bold;
1192
+ font-size: 25px;
1193
+ }
1194
+ .md-desc {
1195
+ padding-left: 10px;
1196
+ border-left: 10px solid white;
1197
+ }
1109
1198
 
1110
1199
 
1111
1200
  @media only screen and (max-width: 480px) {
@@ -1244,18 +1333,34 @@ body.dark .ace-editor {
1244
1333
  -->
1245
1334
  <div class="tab-container">
1246
1335
  <div class="tab-nav">
1247
- <button type="button" class="tab-button active" data-tab="launcher">
1248
- <i class="fa-solid fa-cloud-arrow-down" style="margin-right: 5px;"></i>
1249
- Git
1250
- </button>
1251
- <button type="button" class="tab-button" data-tab="cli">
1252
- <i class="fa-solid fa-terminal" style="margin-right: 5px;"></i>
1253
- CLI
1254
- </button>
1255
- <button type="button" class="tab-button" data-tab="project">
1256
- <i class="fa-regular fa-star" style="margin-right: 5px;"></i>
1257
- New project
1258
- </button>
1336
+ <button type="button" class="tab-button active" data-tab="launcher">
1337
+ <div class='row'>
1338
+ <i class="fa-solid fa-cloud-arrow-down" style="margin-right: 5px;"></i>
1339
+ <h5>Git Launcher</h5>
1340
+ </div>
1341
+ <div class='row'>Launch any git-based open source project</div>
1342
+ </button>
1343
+ <button type="button" class="tab-button" data-tab="cli">
1344
+ <div class='row'>
1345
+ <i class="fa-solid fa-terminal" style="margin-right: 5px;"></i>
1346
+ <h5>CLI App Launcher</h5>
1347
+ </div>
1348
+ <div class='row'>Run any Terminal app in the browser.</div>
1349
+ </button>
1350
+ <button type="button" class="tab-button" data-tab="project">
1351
+ <div class='row'>
1352
+ <i class="fa-regular fa-star" style="margin-right: 5px;"></i>
1353
+ <h5>Empty Launcher</h5>
1354
+ </div>
1355
+ <div class='row'>Start a new project with a built-in 1-click launcher.</div>
1356
+ </button>
1357
+ <button type="button" class="tab-button" data-tab="quickcreate">
1358
+ <div class='row'>
1359
+ <i class="fa-solid fa-robot" style="margin-right: 5px;"></i>
1360
+ <h5>AI Engineer</h5>
1361
+ </div>
1362
+ <div class='row'>Let AI build a launcher for anything.</div>
1363
+ </button>
1259
1364
  </div>
1260
1365
 
1261
1366
  <form id="bootstrap-form">
@@ -1264,16 +1369,12 @@ body.dark .ace-editor {
1264
1369
  <!--
1265
1370
  <h2>Create a 1-Click Launcher</h2>
1266
1371
  -->
1372
+ <!--
1267
1373
  <div class="header-description">
1268
1374
  <img class='logo2' src="/pinokio-black.png"/>
1269
1375
  <div class='bubble'>Create a launcher for any open source project via git.</div>
1270
1376
  </div>
1271
- <div class="git-url-input">
1272
- <blockquote>Name</blockquote>
1273
- <div class='explain'>Enter a new folder name to create.</div>
1274
- <input type="text" name="foldername-launcher" id="foldername-launcher" placeholder="folder name">
1275
- </div>
1276
-
1377
+ -->
1277
1378
  <div class="git-url-input">
1278
1379
  <blockquote>Git URL</blockquote>
1279
1380
  <div class='explain'>Enter a git repository URL to clone from.</div>
@@ -1358,15 +1459,12 @@ body.dark .ace-editor {
1358
1459
 
1359
1460
  <!-- CLI Tab -->
1360
1461
  <div class="tab-content" id="cli-tab">
1462
+ <!--
1361
1463
  <div class="header-description">
1362
1464
  <img class='logo2' src="/pinokio-black.png"/>
1363
1465
  <div class='bubble'>Create a launcher for any terminal apps (npx, uvx, etc.)</div>
1364
1466
  </div>
1365
- <div class="git-url-input">
1366
- <blockquote>Name</blockquote>
1367
- <div class='explain'>Enter a new folder name to create.</div>
1368
- <input type="text" name="foldername-cli" id="foldername-cli" placeholder="folder name">
1369
- </div>
1467
+ -->
1370
1468
  <blockquote>Type</blockquote>
1371
1469
  <div class='explain'>Single-command apps vs. Installable apps.</div>
1372
1470
  <div class="project-options">
@@ -1413,15 +1511,12 @@ body.dark .ace-editor {
1413
1511
 
1414
1512
  <!-- Project Tab -->
1415
1513
  <div class="tab-content" id="project-tab">
1514
+ <!--
1416
1515
  <div class="header-description">
1417
1516
  <img class='logo2' src="/pinokio-black.png"/>
1418
1517
  <div class='bubble'>Create a new project with a built-in 1-click launcher</div>
1419
1518
  </div>
1420
- <div class="git-url-input">
1421
- <blockquote>Name</blockquote>
1422
- <div class='explain'>Enter a new folder name to create.</div>
1423
- <input type="text" name="foldername-new" id="foldername-new" placeholder="folder name">
1424
- </div>
1519
+ -->
1425
1520
  <blockquote>Type</blockquote>
1426
1521
  <div class='explain'>Set up a new 1-click launcher project.</div>
1427
1522
  <div class="project-options">
@@ -1499,18 +1594,47 @@ body.dark .ace-editor {
1499
1594
 
1500
1595
  </div>
1501
1596
 
1502
- <!-- AI Prompt Section (shared across both tabs) -->
1503
- <div class="ai-prompt-section" style="margin-top: 2rem;">
1504
- <!--
1505
- <h2>AI Prompt (Optional)</h2>
1506
- <p>Enter a prompt to build your app with AI assistance.</p>
1507
- -->
1508
- <blockquote>What do you want to build?</blockquote>
1509
- <div class='explain'>(Optional) Explain what the project should do. Will be saved as README.md, and will be automatically used by AI Coding tools to build the app.</div>
1597
+ <!-- Quick Create Tab -->
1598
+ <div class="tab-content" id="quickcreate-tab">
1599
+ <div class="ai-prompt-section">
1600
+ <div class='ai-tabs'>
1601
+ <% ai.forEach((item, i) => { %>
1602
+ <div data-title="<%=item.title%>" class='ai-tab <%=i===0 ? "selected" : ""%>'><%=item.title%></div>
1603
+ <% }) %>
1604
+ </div>
1605
+ <div class='ai-container'>
1606
+ <% ai.forEach((item, i) => { %>
1607
+ <div class='ai-tab-content <%=i===0 ? "selected" : ""%>' data-title="<%=item.title%>">
1608
+ <h1><%=item.title%></h1>
1609
+ <div class='md-desc'><%=item.description%></div>
1610
+ <% Object.keys(item.meta).forEach((key) => { %>
1611
+ <input class='ai-input' type='text' placeholder='<%=item.meta[key]%>' name='<%=key%>'>
1612
+ <% }) %>
1613
+ <textarea><%=item.content%></textarea>
1614
+ <pre class='hidden'><%=item.content%></pre>
1615
+ </div>
1616
+ <% }) %>
1617
+ </div>
1510
1618
  <!--
1511
- <blockquote>The prompt will be stored in README.md, and ANY AI coding tool will be automatically instructed about the project (Just say "build the app").</blockquote>
1512
- -->
1513
- <div id="aiPrompt" class="ace-editor" data-name="aiPrompt" data-placeholder="Describe what you want to build... (optional)" style="height: 150px;"></div>
1619
+ <blockquote>What do you want to build?</blockquote>
1620
+ -->
1621
+ <textarea class='hidden' id='aiPrompt' data-name="aiPrompt" data-placeholder="Describe what you want to build..." placeholder="What do you want to build?"></textarea>
1622
+ <!--
1623
+ <div id="aiPrompt" class="ace-editor" data-name="aiPrompt" data-placeholder="Describe what you want to build..." style="height: 150px;"></div>
1624
+ -->
1625
+
1626
+ <!--
1627
+ <div class='explain'>
1628
+ <div class='well'>
1629
+ <ol>
1630
+ <li>Describe what the project should do, and click "create".</li>
1631
+ <li>This will be saved inside README.md.</li>
1632
+ <li>AI coding tools will automatically read the README.md file to figure out what to build. You just need to tell them to "build".</li>
1633
+ </ol>
1634
+ </div>
1635
+ </div>
1636
+ -->
1637
+ </div>
1514
1638
  </div>
1515
1639
 
1516
1640
  <div class="button-group" style="margin-top: 2rem;">
@@ -1523,9 +1647,48 @@ body.dark .ace-editor {
1523
1647
  </div>
1524
1648
  </main>
1525
1649
  <script>
1650
+ const resizeAITextarea = () => {
1651
+ let textarea = document.querySelector(".ai-container .ai-tab-content.selected textarea")
1652
+ textarea.style.height = "";
1653
+ textarea.style.height = textarea.scrollHeight + "px";
1654
+ }
1526
1655
  document.addEventListener('DOMContentLoaded', function() {
1527
1656
  // Initialize tabs
1528
1657
  initializeTabs();
1658
+
1659
+ document.querySelector(".ai-tabs").addEventListener("click", (e) => {
1660
+ document.querySelectorAll(".ai-tab").forEach((el) => {
1661
+ el.classList.remove('selected')
1662
+ })
1663
+ e.target.classList.add("selected")
1664
+ document.querySelectorAll(`.ai-tab-content`).forEach((el) => {
1665
+ el.classList.remove("selected")
1666
+ })
1667
+ let title = e.target.getAttribute("data-title")
1668
+ document.querySelector(`.ai-tab-content[data-title="${title}"]`).classList.add('selected')
1669
+ resizeAITextarea()
1670
+ })
1671
+ document.querySelectorAll(".ai-tab-content").forEach((tab) => {
1672
+ tab.addEventListener("input", (e) => {
1673
+ if (e.target.classList.contains("ai-input")) {
1674
+ let name = e.target.name
1675
+ // get all values from all ai-input and replace
1676
+ let els = e.target.closest(".ai-tab-content").querySelectorAll(".ai-input")
1677
+ let vals = {}
1678
+ let pre = e.target.closest(".ai-tab-content").querySelector("pre")
1679
+ let textarea = e.target.closest(".ai-tab-content").querySelector("textarea")
1680
+ resizeAITextarea()
1681
+ let md = pre.textContent
1682
+ for(let el of els) {
1683
+ let pattern = `{{${el.name}}}`
1684
+ md = md.replaceAll(pattern, el.value)
1685
+ }
1686
+ textarea.value = md
1687
+ document.querySelector("#aiPrompt").value = md
1688
+ }
1689
+ })
1690
+ })
1691
+
1529
1692
 
1530
1693
  // Show launcher tab by default
1531
1694
  showTab('launcher');
@@ -1680,6 +1843,11 @@ function showTab(tabName) {
1680
1843
  if (tabName !== 'cli') {
1681
1844
  clearCliInputs();
1682
1845
  }
1846
+
1847
+ if (tabName === "quickcreate") {
1848
+ resizeAITextarea()
1849
+ }
1850
+
1683
1851
  }
1684
1852
 
1685
1853
  function hideAllConditionalOptions() {
@@ -1878,7 +2046,8 @@ function validateForm() {
1878
2046
  const activeTab = document.querySelector('.tab-content.active').id;
1879
2047
  const isLauncher = activeTab === 'launcher-tab';
1880
2048
  const isCli = activeTab === 'cli-tab';
1881
- const gitUrl = document.getElementById('gitUrl').value.trim();
2049
+ const isQuickCreate = activeTab === 'quickcreate-tab';
2050
+ const gitUrl = document.getElementById('gitUrl').value.trim();
1882
2051
 
1883
2052
  // Validate Git URL for launcher tab
1884
2053
  if (isLauncher) {
@@ -1887,15 +2056,6 @@ function validateForm() {
1887
2056
  alert('Please enter a Git repository URL.');
1888
2057
  return false;
1889
2058
  }
1890
- const name = document.getElementById('foldername-launcher').value.trim();
1891
- if (!name) {
1892
- alert('Please enter a folder name.');
1893
- return false;
1894
- }
1895
- if (name.includes(" ")) {
1896
- alert('No spaces allowed');
1897
- return false;
1898
- }
1899
2059
 
1900
2060
  const projectType = document.querySelector('input[name="projectType"]:checked');
1901
2061
  if (!projectType) {
@@ -1911,16 +2071,6 @@ function validateForm() {
1911
2071
  alert('Please select a CLI type.');
1912
2072
  return false;
1913
2073
  }
1914
-
1915
- const name = document.getElementById('foldername-cli').value.trim();
1916
- if (!name) {
1917
- alert('Please enter a folder name.');
1918
- return false;
1919
- }
1920
- if (name.includes(" ")) {
1921
- alert('No spaces allowed');
1922
- return false;
1923
- }
1924
2074
 
1925
2075
  if (cliType.value === 'instant') {
1926
2076
  const launchCommand = document.querySelector('input[name="launchCommand"]').value.trim();
@@ -1942,23 +2092,18 @@ function validateForm() {
1942
2092
  }
1943
2093
  }
1944
2094
 
2095
+ // Validate quick create tab
2096
+ if (isQuickCreate) {
2097
+ // No additional validation needed for quick create
2098
+ }
2099
+
1945
2100
  // Validate project tab
1946
- if (!isLauncher && !isCli) {
2101
+ if (!isLauncher && !isCli && !isQuickCreate) {
1947
2102
  const projectType = document.querySelector('input[name="projectType"]:checked');
1948
2103
  if (!projectType) {
1949
2104
  alert('Please select a project type.');
1950
2105
  return false;
1951
2106
  }
1952
-
1953
- const name = document.getElementById('foldername-new').value.trim();
1954
- if (!name) {
1955
- alert('Please enter a folder name.');
1956
- return false;
1957
- }
1958
- if (name.includes(" ")) {
1959
- alert('No spaces allowed');
1960
- return false;
1961
- }
1962
2107
 
1963
2108
  // Validate Documentation options
1964
2109
  if (projectType.value === 'documentation') {
@@ -1994,10 +2139,28 @@ function handleSubmit(event) {
1994
2139
  return;
1995
2140
  }
1996
2141
 
2142
+ // Prompt for folder name
2143
+ let name = prompt('Enter a folder name to create:');
2144
+ if (!name) {
2145
+ return; // User cancelled or entered empty name
2146
+ }
2147
+
2148
+ name = name.trim();
2149
+ if (!name) {
2150
+ alert('Please enter a valid folder name.');
2151
+ return;
2152
+ }
2153
+
2154
+ if (name.includes(' ')) {
2155
+ alert('No spaces allowed in folder names.');
2156
+ return;
2157
+ }
2158
+
1997
2159
  const formData = new FormData(event.target);
1998
2160
  const activeTab = document.querySelector('.tab-content.active').id;
1999
2161
  const isLauncher = activeTab === 'launcher-tab';
2000
2162
  const isCli = activeTab === 'cli-tab';
2163
+ const isQuickCreate = activeTab === 'quickcreate-tab';
2001
2164
  const gitUrl = isLauncher ? formData.get('gitUrl').trim() : '';
2002
2165
 
2003
2166
  // Get values from Ace editors and clean them
@@ -2005,22 +2168,26 @@ function handleSubmit(event) {
2005
2168
  const cloneStartCommandText = aceEditors['cloneStartCommand'] ? aceEditors['cloneStartCommand'].getValue().trim() : '';
2006
2169
  const installCommandText = aceEditors['installCommand'] ? aceEditors['installCommand'].getValue().trim() : '';
2007
2170
  const installableLaunchCommandText = aceEditors['installableLaunchCommand'] ? aceEditors['installableLaunchCommand'].getValue().trim() : '';
2008
- const aiPromptText = aceEditors['aiPrompt'] ? aceEditors['aiPrompt'].getValue().trim() : '';
2171
+
2172
+ // Only get AI prompt text if Quick Create tab is active
2173
+ // const aiPromptText = isQuickCreate && aceEditors['aiPrompt'] ? aceEditors['aiPrompt'].getValue().trim() : '';
2174
+ const aiPromptText = isQuickCreate && document.querySelector("#aiPrompt") ? document.querySelector("#aiPrompt").value.trim() : '';
2009
2175
 
2010
2176
  // Set startType and projectType based on active tab
2011
- let startType, projectType, name;
2177
+ let startType, projectType;
2012
2178
  if (isLauncher) {
2013
2179
  startType = 'clone';
2014
2180
  projectType = formData.get('projectType');
2015
- name = formData.get("foldername-launcher").trim();
2016
2181
  } else if (isCli) {
2017
2182
  startType = 'new';
2018
2183
  projectType = 'cli';
2019
- name = formData.get("foldername-cli").trim();
2184
+ } else if (isQuickCreate) {
2185
+ startType = 'new';
2186
+ //projectType = 'empty';
2187
+ projectType = 'ai';
2020
2188
  } else {
2021
2189
  startType = 'new';
2022
2190
  projectType = formData.get('projectType');
2023
- name = formData.get("foldername-new").trim();
2024
2191
  }
2025
2192
 
2026
2193
  // For CLI category, map the projectType to cliType for backwards compatibility
@@ -430,16 +430,13 @@ body.dark .net {
430
430
  </div>
431
431
  </h1>
432
432
  </header>
433
- <form class='search'>
434
- <div class='app-btns'>
435
- <a class='btn create-new' href="/init"><i class="fa-solid fa-plus"></i> Build</a>
436
- <a class='btn' id='explore' href="/?mode=explore"><i class="fa-solid fa-magnifying-glass"></i> Discover</a>
437
- </div>
438
- <input type='search' class="flexible" placeholder='Filter network apps'>
439
- </form>
440
433
  <main>
441
434
  <div id='terminal' class='hidden'></div>
442
435
  <aside>
436
+ <div class='btn-tab'>
437
+ <a href="/init" class='btn'><i class="fa-solid fa-plus"></i> Create</a>
438
+ <a class='btn' id='explore' href="/?mode=explore"><i class="fa-solid fa-globe"></i> Discover</a>
439
+ </div>
443
440
  <a href="/" class='tab'><i class='fas fa-laptop-code'></i> Local</a>
444
441
  <a href="/network" class='tab'><i class="fa-solid fa-wifi"></i> Network</a>
445
442
  <% if (list.length > 0) { %>
@@ -452,9 +449,17 @@ body.dark .net {
452
449
  <% } %>
453
450
  <% }) %>
454
451
  <% } %>
455
- <div class='tab'><i class="fa-solid fa-code"></i> Dev</div>
456
452
  </aside>
457
453
  <div class='container'>
454
+ <form class='search'>
455
+ <!--
456
+ <div class='app-btns'>
457
+ <a class='btn create-new' href="/init"><i class="fa-solid fa-plus"></i> Build</a>
458
+ <a class='btn' id='explore' href="/?mode=explore"><i class="fa-solid fa-magnifying-glass"></i> Discover</a>
459
+ </div>
460
+ -->
461
+ <input type='search' class="flexible" placeholder='Search apps'>
462
+ </form>
458
463
  <div class='running-apps'>
459
464
  <% if (current_host === host) { %>
460
465
  <div class='section-header'>
@@ -131,8 +131,10 @@ a.badge {
131
131
  text-decoration: none;
132
132
  }
133
133
  body.dark .btn {
134
- color: white;
134
+ background: rgba(255,255,255,0.05) !important;
135
+ /*
135
136
  border: 1px solid rgba(255,255,255,0.3);
137
+ */
136
138
  }
137
139
  body.dark .badge {
138
140
  background: rgba(255,255,255,0.1);
@@ -278,7 +280,7 @@ body.dark .config {
278
280
  background: rgba(0,0,0,0.9);
279
281
  */
280
282
  color: black;
281
- padding: 10px 100px 0;
283
+ padding: 50px 50px 0;
282
284
  text-align: left;
283
285
  }
284
286
  .config-header {
@@ -565,6 +567,7 @@ input[type=search] {
565
567
  </div>
566
568
  </h1>
567
569
  </header>
570
+ <!--
568
571
  <form class='search'>
569
572
  <div class='app-btns'>
570
573
  <a class='btn create-new' href="/init"><i class="fa-solid fa-plus"></i> Build</a>
@@ -572,8 +575,13 @@ input[type=search] {
572
575
  </div>
573
576
  <input type='search' class="flexible" placeholder='Filter network apps'>
574
577
  </form>
578
+ -->
575
579
  <main>
576
580
  <aside>
581
+ <div class='btn-tab'>
582
+ <a href="/init" class='btn'><i class="fa-solid fa-plus"></i> Create</a>
583
+ <a class='btn' id='explore' href="/?mode=explore"><i class="fa-solid fa-globe"></i> Discover</a>
584
+ </div>
577
585
  <a href="/" class='tab'><i class='fas fa-laptop-code'></i> Local</a>
578
586
  <a href="/network" class='tab selected'><i class="fa-solid fa-wifi"></i> Network</a>
579
587
  <% if (list.length > 0) { %>
@@ -582,7 +590,6 @@ input[type=search] {
582
590
  <a href="/net/<%=name%>" class='submenu tab'><i class="fa-brands fa-<%=brands[platform]%>"></i> <%=name%> (<%=current_host === host ? 'this machine' : host%>)</a>
583
591
  <% }) %>
584
592
  <% } %>
585
- <div class='tab'><i class="fa-solid fa-code"></i> Dev</div>
586
593
  </aside>
587
594
  <div class='container'>
588
595
  <div class='config'>
@@ -15,7 +15,7 @@
15
15
  <div <%=item.default ? 'data-default' : ''%> data-action="<%=JSON.stringify(item.action)%>" class='btn header-item frame-link <%=item.display || ""%>'>
16
16
  <div class='tab'><%-item.btn%></div>
17
17
  </div>
18
- <% } else if (item.run) { %>
18
+ <% } else if (item.run && typeof item.run === 'string') { %>
19
19
  <div data-run="<%=item.run%>" data-cwd="<%=item.cwd%>" class='btn header-item frame-link <%=item.display || ""%>'>
20
20
  <div class='tab'><%-item.btn%></div>
21
21
  </div>
@@ -36,6 +36,9 @@
36
36
  </a>
37
37
  <% } else { %>
38
38
  <a <%=item.default ? 'data-default' : ''%> data-confirm="<%=item.confirm%>" data-index="<%=index+4%>" target="<%=item.target%>" data-mode="<%=item.mode%>" href="<%=item.href%>" class='btn header-item frame-link <%=item.display || ""%>' <%=item.shell_id ? `data-shell=${item.shell_id}` : ""%> <%=item.script_id ? `data-script=${item.script_id}` : ''%>>
39
+ <% if (item.running) { %>
40
+ <i class="fa-solid fa-circle"></i>
41
+ <% } %>
39
42
  <div class='tab'><%-item.btn%></div>
40
43
  <% if (item.running) { %>
41
44
  <% if (item.online) { %>
@@ -51,6 +51,9 @@
51
51
  </a>
52
52
  <% } else { %>
53
53
  <a <%=item.default ? 'data-default' : ''%> data-confirm="<%=item.confirm%>" data-index="<%=index%>" target="<%=item.target%>" data-mode="<%=item.mode%>" href="<%=item.href%>" class='btn header-item frame-link <%=item.display || ""%>' <%=item.shell_id ? `data-shell=${item.shell_id}` : ""%> <%=item.script_id ? `data-script=${item.script_id}` : ''%>>
54
+ <% if (item.running) { %>
55
+ <i class="fa-solid fa-circle"></i>
56
+ <% } %>
54
57
  <div class='tab'><%-item.btn%></div>
55
58
  <% if (item.running) { %>
56
59
  <!--
@@ -0,0 +1,22 @@
1
+ <% dynamic.forEach((item, index) => { %>
2
+ <a <%=item.default ? 'data-default' : ''%> data-confirm="<%=item.confirm%>" data-index="<%=index+4%>" target="<%=item.target%>" data-mode="<%=item.mode%>" href="<%=item.href%>" class='btn header-item frame-link <%=item.display || ""%>' <%=item.shell_id ? `data-shell=${item.shell_id}` : ""%> <%=item.script_id ? `data-script=${item.script_id}` : ''%>>
3
+ <% if (item.running) { %>
4
+ <i class="fa-solid fa-circle"></i>
5
+ <% } %>
6
+ <div class='tab'><%-item.btn%></div>
7
+ <% if (item.running) { %>
8
+ <% if (item.online) { %>
9
+ <div class='loader shutdown online'>
10
+ </div>
11
+ <% } else { %>
12
+ <div class='loader shutdown'>
13
+ <button class='btn'>
14
+ <i class="fa-solid fa-square"></i> Stop
15
+ </button>
16
+ </div>
17
+ <% } %>
18
+ <% } else { %>
19
+ <i class="fa-solid fa-angle-right"></i>
20
+ <% } %>
21
+ </a>
22
+ <% }) %>
@@ -417,7 +417,7 @@ body.dark .keys pre {
417
417
  <div>Keys are array values stored for unique host names, stored at <code>~/pinokio/key.json</code>, and can be set through <code>pre</code> attributes in pinokio scripts. Here's an example:</div>
418
418
  <pre>{
419
419
  "openai.com": [
420
- "sk-proj-2najBvCGFx18belpjf13T3BlbkFJIAR5KXK031n41WzAsPEn"
420
+ "12345678-FAKEaccesstoken1234567890abcdef"
421
421
  ],
422
422
  "x.com": [
423
423
  "12345678-FAKEaccesstoken1234567890abcdef",