pinokiod 3.24.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,24 +1333,34 @@ body.dark .ace-editor {
1244
1333
  -->
1245
1334
  <div class="tab-container">
1246
1335
  <div class="tab-nav">
1247
- <!--
1248
- <button type="button" class="tab-button active" data-tab="launcher">
1249
- <i class="fa-solid fa-robot" style="margin-right: 5px;"></i>
1250
- Quick Launcher
1251
- </button>
1252
- -->
1253
- <button type="button" class="tab-button active" data-tab="launcher">
1254
- <i class="fa-solid fa-cloud-arrow-down" style="margin-right: 5px;"></i>
1255
- Git Project Launcher
1256
- </button>
1257
- <button type="button" class="tab-button" data-tab="cli">
1258
- <i class="fa-solid fa-terminal" style="margin-right: 5px;"></i>
1259
- CLI App Launcher
1260
- </button>
1261
- <button type="button" class="tab-button" data-tab="project">
1262
- <i class="fa-regular fa-star" style="margin-right: 5px;"></i>
1263
- New project
1264
- </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>
1265
1364
  </div>
1266
1365
 
1267
1366
  <form id="bootstrap-form">
@@ -1270,16 +1369,12 @@ body.dark .ace-editor {
1270
1369
  <!--
1271
1370
  <h2>Create a 1-Click Launcher</h2>
1272
1371
  -->
1372
+ <!--
1273
1373
  <div class="header-description">
1274
1374
  <img class='logo2' src="/pinokio-black.png"/>
1275
1375
  <div class='bubble'>Create a launcher for any open source project via git.</div>
1276
1376
  </div>
1277
- <div class="git-url-input">
1278
- <blockquote>Name</blockquote>
1279
- <div class='explain'>Enter a new folder name to create.</div>
1280
- <input type="text" name="foldername-launcher" id="foldername-launcher" placeholder="folder name">
1281
- </div>
1282
-
1377
+ -->
1283
1378
  <div class="git-url-input">
1284
1379
  <blockquote>Git URL</blockquote>
1285
1380
  <div class='explain'>Enter a git repository URL to clone from.</div>
@@ -1364,15 +1459,12 @@ body.dark .ace-editor {
1364
1459
 
1365
1460
  <!-- CLI Tab -->
1366
1461
  <div class="tab-content" id="cli-tab">
1462
+ <!--
1367
1463
  <div class="header-description">
1368
1464
  <img class='logo2' src="/pinokio-black.png"/>
1369
1465
  <div class='bubble'>Create a launcher for any terminal apps (npx, uvx, etc.)</div>
1370
1466
  </div>
1371
- <div class="git-url-input">
1372
- <blockquote>Name</blockquote>
1373
- <div class='explain'>Enter a new folder name to create.</div>
1374
- <input type="text" name="foldername-cli" id="foldername-cli" placeholder="folder name">
1375
- </div>
1467
+ -->
1376
1468
  <blockquote>Type</blockquote>
1377
1469
  <div class='explain'>Single-command apps vs. Installable apps.</div>
1378
1470
  <div class="project-options">
@@ -1419,15 +1511,12 @@ body.dark .ace-editor {
1419
1511
 
1420
1512
  <!-- Project Tab -->
1421
1513
  <div class="tab-content" id="project-tab">
1514
+ <!--
1422
1515
  <div class="header-description">
1423
1516
  <img class='logo2' src="/pinokio-black.png"/>
1424
1517
  <div class='bubble'>Create a new project with a built-in 1-click launcher</div>
1425
1518
  </div>
1426
- <div class="git-url-input">
1427
- <blockquote>Name</blockquote>
1428
- <div class='explain'>Enter a new folder name to create.</div>
1429
- <input type="text" name="foldername-new" id="foldername-new" placeholder="folder name">
1430
- </div>
1519
+ -->
1431
1520
  <blockquote>Type</blockquote>
1432
1521
  <div class='explain'>Set up a new 1-click launcher project.</div>
1433
1522
  <div class="project-options">
@@ -1505,18 +1594,47 @@ body.dark .ace-editor {
1505
1594
 
1506
1595
  </div>
1507
1596
 
1508
- <!-- AI Prompt Section (shared across both tabs) -->
1509
- <div class="ai-prompt-section" style="margin-top: 2rem;">
1510
- <!--
1511
- <h2>AI Prompt (Optional)</h2>
1512
- <p>Enter a prompt to build your app with AI assistance.</p>
1513
- -->
1514
- <blockquote>What do you want to build?</blockquote>
1515
- <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>
1516
1618
  <!--
1517
- <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>
1518
- -->
1519
- <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>
1520
1638
  </div>
1521
1639
 
1522
1640
  <div class="button-group" style="margin-top: 2rem;">
@@ -1529,9 +1647,48 @@ body.dark .ace-editor {
1529
1647
  </div>
1530
1648
  </main>
1531
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
+ }
1532
1655
  document.addEventListener('DOMContentLoaded', function() {
1533
1656
  // Initialize tabs
1534
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
+
1535
1692
 
1536
1693
  // Show launcher tab by default
1537
1694
  showTab('launcher');
@@ -1686,6 +1843,11 @@ function showTab(tabName) {
1686
1843
  if (tabName !== 'cli') {
1687
1844
  clearCliInputs();
1688
1845
  }
1846
+
1847
+ if (tabName === "quickcreate") {
1848
+ resizeAITextarea()
1849
+ }
1850
+
1689
1851
  }
1690
1852
 
1691
1853
  function hideAllConditionalOptions() {
@@ -1884,7 +2046,8 @@ function validateForm() {
1884
2046
  const activeTab = document.querySelector('.tab-content.active').id;
1885
2047
  const isLauncher = activeTab === 'launcher-tab';
1886
2048
  const isCli = activeTab === 'cli-tab';
1887
- const gitUrl = document.getElementById('gitUrl').value.trim();
2049
+ const isQuickCreate = activeTab === 'quickcreate-tab';
2050
+ const gitUrl = document.getElementById('gitUrl').value.trim();
1888
2051
 
1889
2052
  // Validate Git URL for launcher tab
1890
2053
  if (isLauncher) {
@@ -1893,15 +2056,6 @@ function validateForm() {
1893
2056
  alert('Please enter a Git repository URL.');
1894
2057
  return false;
1895
2058
  }
1896
- const name = document.getElementById('foldername-launcher').value.trim();
1897
- if (!name) {
1898
- alert('Please enter a folder name.');
1899
- return false;
1900
- }
1901
- if (name.includes(" ")) {
1902
- alert('No spaces allowed');
1903
- return false;
1904
- }
1905
2059
 
1906
2060
  const projectType = document.querySelector('input[name="projectType"]:checked');
1907
2061
  if (!projectType) {
@@ -1917,16 +2071,6 @@ function validateForm() {
1917
2071
  alert('Please select a CLI type.');
1918
2072
  return false;
1919
2073
  }
1920
-
1921
- const name = document.getElementById('foldername-cli').value.trim();
1922
- if (!name) {
1923
- alert('Please enter a folder name.');
1924
- return false;
1925
- }
1926
- if (name.includes(" ")) {
1927
- alert('No spaces allowed');
1928
- return false;
1929
- }
1930
2074
 
1931
2075
  if (cliType.value === 'instant') {
1932
2076
  const launchCommand = document.querySelector('input[name="launchCommand"]').value.trim();
@@ -1948,23 +2092,18 @@ function validateForm() {
1948
2092
  }
1949
2093
  }
1950
2094
 
2095
+ // Validate quick create tab
2096
+ if (isQuickCreate) {
2097
+ // No additional validation needed for quick create
2098
+ }
2099
+
1951
2100
  // Validate project tab
1952
- if (!isLauncher && !isCli) {
2101
+ if (!isLauncher && !isCli && !isQuickCreate) {
1953
2102
  const projectType = document.querySelector('input[name="projectType"]:checked');
1954
2103
  if (!projectType) {
1955
2104
  alert('Please select a project type.');
1956
2105
  return false;
1957
2106
  }
1958
-
1959
- const name = document.getElementById('foldername-new').value.trim();
1960
- if (!name) {
1961
- alert('Please enter a folder name.');
1962
- return false;
1963
- }
1964
- if (name.includes(" ")) {
1965
- alert('No spaces allowed');
1966
- return false;
1967
- }
1968
2107
 
1969
2108
  // Validate Documentation options
1970
2109
  if (projectType.value === 'documentation') {
@@ -2000,10 +2139,28 @@ function handleSubmit(event) {
2000
2139
  return;
2001
2140
  }
2002
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
+
2003
2159
  const formData = new FormData(event.target);
2004
2160
  const activeTab = document.querySelector('.tab-content.active').id;
2005
2161
  const isLauncher = activeTab === 'launcher-tab';
2006
2162
  const isCli = activeTab === 'cli-tab';
2163
+ const isQuickCreate = activeTab === 'quickcreate-tab';
2007
2164
  const gitUrl = isLauncher ? formData.get('gitUrl').trim() : '';
2008
2165
 
2009
2166
  // Get values from Ace editors and clean them
@@ -2011,22 +2168,26 @@ function handleSubmit(event) {
2011
2168
  const cloneStartCommandText = aceEditors['cloneStartCommand'] ? aceEditors['cloneStartCommand'].getValue().trim() : '';
2012
2169
  const installCommandText = aceEditors['installCommand'] ? aceEditors['installCommand'].getValue().trim() : '';
2013
2170
  const installableLaunchCommandText = aceEditors['installableLaunchCommand'] ? aceEditors['installableLaunchCommand'].getValue().trim() : '';
2014
- 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() : '';
2015
2175
 
2016
2176
  // Set startType and projectType based on active tab
2017
- let startType, projectType, name;
2177
+ let startType, projectType;
2018
2178
  if (isLauncher) {
2019
2179
  startType = 'clone';
2020
2180
  projectType = formData.get('projectType');
2021
- name = formData.get("foldername-launcher").trim();
2022
2181
  } else if (isCli) {
2023
2182
  startType = 'new';
2024
2183
  projectType = 'cli';
2025
- name = formData.get("foldername-cli").trim();
2184
+ } else if (isQuickCreate) {
2185
+ startType = 'new';
2186
+ //projectType = 'empty';
2187
+ projectType = 'ai';
2026
2188
  } else {
2027
2189
  startType = 'new';
2028
2190
  projectType = formData.get('projectType');
2029
- name = formData.get("foldername-new").trim();
2030
2191
  }
2031
2192
 
2032
2193
  // For CLI category, map the projectType to cliType for backwards compatibility
@@ -449,7 +449,6 @@ body.dark .net {
449
449
  <% } %>
450
450
  <% }) %>
451
451
  <% } %>
452
- <div class='tab'><i class="fa-solid fa-code"></i> Dev</div>
453
452
  </aside>
454
453
  <div class='container'>
455
454
  <form class='search'>
@@ -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);
@@ -588,7 +590,6 @@ input[type=search] {
588
590
  <a href="/net/<%=name%>" class='submenu tab'><i class="fa-brands fa-<%=brands[platform]%>"></i> <%=name%> (<%=current_host === host ? 'this machine' : host%>)</a>
589
591
  <% }) %>
590
592
  <% } %>
591
- <div class='tab'><i class="fa-solid fa-code"></i> Dev</div>
592
593
  </aside>
593
594
  <div class='container'>
594
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>