pinokiod 3.19.79 → 3.19.81

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/kernel/bin/cli.js CHANGED
@@ -9,36 +9,31 @@ class CLI {
9
9
  }, ondata)
10
10
  }
11
11
  async installed(req, ondata) {
12
- console.log("Check CLI installed")
13
-
14
12
  let exists
15
- console.log("platform", this.kernel.platform)
16
13
  if (this.kernel.platform === "win32") {
17
14
  exists = await Util.exists(this.kernel.path("bin/npm/pterm"))
18
- console.log("Exists", exists)
19
15
  } else {
20
16
  exists = await Util.exists(this.kernel.path("bin/npm/bin/pterm"))
21
- console.log("Exists", exists)
22
17
  }
23
-
24
18
  if (exists) {
25
19
  let p = this.kernel.which("pterm")
26
20
  if (p) {
27
21
  let res = await this.kernel.exec({
28
22
  message: "pterm version terminal"
29
23
  }, ondata)
30
- let v = res.stdout.replace("pterm@", "")
31
- let coerced = semver.coerce(v)
32
- console.log({ v, coerced })
33
- if (semver.satisfies(coerced, this.version)) {
34
- console.log("Installed")
35
- return true
24
+ let e = /pterm@([0-9.]+)/.exec(res.stdout)
25
+ if (e && e.length > 0) {
26
+ let v = e[1]
27
+ let coerced = semver.coerce(v)
28
+ if (semver.satisfies(coerced, this.version)) {
29
+ return true
30
+ } else {
31
+ return false
32
+ }
36
33
  } else {
37
- console.log("Not Installed")
38
34
  return false
39
35
  }
40
36
  } else {
41
- console.log("FALSE")
42
37
  return false
43
38
  }
44
39
  } else {
@@ -122,12 +122,15 @@ module.exports = {
122
122
  { name: "node", },
123
123
  { name: "cli", },
124
124
  { name: "uv", },
125
+ { name: "caddy", },
126
+ { name: "py", },
125
127
  ])
126
128
  let conda_requirements = [
127
129
  zip_cmd,
128
130
  "uv",
129
131
  "node",
130
132
  "git",
133
+ "caddy",
131
134
  ]
132
135
  if (platform === "win32") {
133
136
  requirements.push({ name: "registry" })
@@ -1,7 +1,3 @@
1
- ---
2
-
3
- # Usage
4
-
5
1
  When you log into Github here, the login will be used for the git installed within Pinokio.
6
2
 
7
3
  Logging into Github lets you do things like:
@@ -36,12 +36,12 @@ class Proto {
36
36
  process.stdout.write(e.raw)
37
37
  })
38
38
  }
39
- let exists3 = await this.kernel.exists("prototype/CLI.md")
39
+ let exists3 = await this.kernel.exists("prototype/PTERM.md")
40
40
  if (!exists3) {
41
41
  await this.kernel.download({
42
42
  uri: "https://raw.githubusercontent.com/pinokiocomputer/pterm/refs/heads/main/README.md",
43
43
  path: this.kernel.path("prototype"),
44
- filename: "CLI.md"
44
+ filename: "PTERM.md"
45
45
  }, (e) => {
46
46
  process.stdout.write(e.raw)
47
47
  })
@@ -73,9 +73,9 @@ class Proto {
73
73
  let readme_path = this.kernel.path("prototype/PINOKIO.md")
74
74
  await fs.promises.cp(readme_path, path.resolve(cwd, name, "PINOKIO.md"))
75
75
 
76
- // copy cli.md
77
- let cli_readme_path = this.kernel.path("prototype/CLI.md")
78
- await fs.promises.cp(cli_readme_path, path.resolve(cwd, name, "CLI.md"))
76
+ // copy pterm.md
77
+ let cli_readme_path = this.kernel.path("prototype/PTERM.md")
78
+ await fs.promises.cp(cli_readme_path, path.resolve(cwd, name, "PTERM.md"))
79
79
 
80
80
 
81
81
  return { success: "/p/" + name }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.19.79",
3
+ "version": "3.19.81",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -367,7 +367,7 @@ class Server {
367
367
 
368
368
  async chrome(req, res, type) {
369
369
  let d = Date.now()
370
- console.time("1 chrome " + d)
370
+ // console.time("1 chrome " + d)
371
371
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
372
372
  bin: this.kernel.bin.preset("dev"),
373
373
  })
@@ -375,7 +375,7 @@ class Server {
375
375
  res.redirect(`/setup/dev?callback=${req.originalUrl}`)
376
376
  return
377
377
  }
378
- console.timeEnd("1 chrome " + d)
378
+ // console.timeEnd("1 chrome " + d)
379
379
 
380
380
  let name = req.params.name
381
381
  let config = await this.kernel.api.meta(name)
@@ -490,22 +490,22 @@ class Server {
490
490
  // }
491
491
  // }
492
492
 
493
- console.time("2 chrome " + d)
493
+ // console.time("2 chrome " + d)
494
494
  await this.init_env("api/" + name)
495
- console.timeEnd("2 chrome " + d)
495
+ // console.timeEnd("2 chrome " + d)
496
496
 
497
- console.time("3 chrome " + d)
497
+ // console.time("3 chrome " + d)
498
498
  let mode = "run"
499
499
  if (req.query && req.query.mode) {
500
500
  mode = req.query.mode
501
501
  }
502
502
  const env = await this.kernel.env("api/" + name)
503
- console.timeEnd("3 chrome " + d)
503
+ // console.timeEnd("3 chrome " + d)
504
504
 
505
505
  // profile + feed
506
506
  const repositoryPath = path.resolve(this.kernel.api.userdir, name)
507
507
 
508
- console.time("4 chrome " + d)
508
+ // console.time("4 chrome " + d)
509
509
  try {
510
510
  await git.resolveRef({ fs, dir: repositoryPath, ref: 'HEAD' });
511
511
  } catch (err) {
@@ -514,9 +514,9 @@ class Server {
514
514
  await git.init({ fs, dir: repositoryPath });
515
515
  }
516
516
 
517
- console.timeEnd("4 chrome " + d)
517
+ // console.timeEnd("4 chrome " + d)
518
518
 
519
- console.time("5 chrome " + d)
519
+ // console.time("5 chrome " + d)
520
520
  let gitRemote = await git.getConfig({ fs, http, dir: repositoryPath, path: 'remote.origin.url' })
521
521
  let profile
522
522
  let feed
@@ -530,20 +530,24 @@ class Server {
530
530
  profile = this.profile(gitRemote)
531
531
  feed = this.newsfeed(gitRemote)
532
532
  }
533
- console.timeEnd("5 chrome " + d)
533
+ // console.timeEnd("5 chrome " + d)
534
534
 
535
535
  // git
536
536
 
537
537
  let c = this.kernel.path("api", name)
538
538
 
539
- console.time("6 chrome " + d)
539
+ // console.time("6 chrome " + d)
540
540
  await this.kernel.plugin.init()
541
- let plugin = await this.getPlugin(name)
542
- let plugin_menu = null
543
- if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
544
- let running_dynamic = this.running_dynamic(name, plugin.menu)
545
- plugin_menu = plugin.menu.concat(running_dynamic)
546
- }
541
+ // console.timeEnd("6 chrome " + d)
542
+ // console.time("7 chrome " + d)
543
+ // let plugin = await this.getPlugin(name)
544
+ // console.timeEnd("7 chrome " + d)
545
+ // console.time("8 chrome " + d)
546
+ // let plugin_menu = null
547
+ // if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
548
+ // let running_dynamic = this.running_dynamic(name, plugin.menu)
549
+ // plugin_menu = plugin.menu.concat(running_dynamic)
550
+ // }
547
551
 
548
552
 
549
553
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
@@ -553,7 +557,7 @@ class Server {
553
557
  current_urls,
554
558
  path: this.kernel.path("api", name),
555
559
  log_path: this.kernel.path("api", name, "logs"),
556
- plugin_menu,
560
+ plugin_menu: null,
557
561
  portal: this.portal,
558
562
  install: this.install,
559
563
  error: err,
@@ -567,6 +571,7 @@ class Server {
567
571
  memory: this.kernel.memory,
568
572
  sidebar: "/pinokio/sidebar/" + name,
569
573
  repos: "/pinokio/repos/" + name,
574
+ ai: "/pinokio/ai/" + name,
570
575
  dynamic: "/pinokio/dynamic/" + name,
571
576
  // dynamic: "/pinokio/dynamic/" + name,
572
577
  dynamic_content: null,
@@ -585,12 +590,12 @@ class Server {
585
590
  execUrl: "/api/" + name,
586
591
  // rawpath,
587
592
  }
588
- console.timeEnd("6 chrome " + d)
589
- console.time("7 chrome " + d)
590
- if (!this.kernel.proto.config) {
591
- await this.kernel.proto.init()
592
- }
593
- console.timeEnd("7 chrome " + d)
593
+ // console.timeEnd("8 chrome " + d)
594
+ // console.time("9 chrome " + d)
595
+ // if (!this.kernel.proto.config) {
596
+ // await this.kernel.proto.init()
597
+ // }
598
+ // console.timeEnd("9 chrome " + d)
594
599
  res.render("app", result)
595
600
  }
596
601
  getVariationUrls(req) {
@@ -2443,6 +2448,9 @@ class Server {
2443
2448
  }
2444
2449
  }
2445
2450
  async getPluginGlobal(filepath) {
2451
+ if (!this.kernel.plugin.config) {
2452
+ await this.kernel.plugin.init()
2453
+ }
2446
2454
  if (this.kernel.plugin.config) {
2447
2455
  try {
2448
2456
  let info = new Info(this.kernel)
@@ -2468,17 +2476,16 @@ class Server {
2468
2476
  return plugin
2469
2477
  } catch (e) {
2470
2478
  console.log("getPlugin ERROR", e)
2471
- return {
2472
- menu: []
2473
- }
2479
+ return null
2474
2480
  }
2475
2481
  } else {
2476
- return {
2477
- menu: []
2478
- }
2482
+ return null
2479
2483
  }
2480
2484
  }
2481
2485
  async getPlugin(name) {
2486
+ if (!this.kernel.plugin.config) {
2487
+ await this.kernel.plugin.init()
2488
+ }
2482
2489
  if (this.kernel.plugin.config) {
2483
2490
  try {
2484
2491
  let info = new Info(this.kernel)
@@ -2492,15 +2499,10 @@ class Server {
2492
2499
  return plugin
2493
2500
  } catch (e) {
2494
2501
  console.log("getPlugin ERROR", e)
2495
- return {
2496
- menu: []
2497
- }
2502
+ return null
2498
2503
  }
2499
2504
  } else {
2500
- return {
2501
- menu: []
2502
- }
2503
-
2505
+ return null
2504
2506
  }
2505
2507
  }
2506
2508
  async check_router_up() {
@@ -4422,6 +4424,22 @@ class Server {
4422
4424
  let dir = this.kernel.path("api", req.params[0])
4423
4425
  let config = await this.kernel.git.config(dir)
4424
4426
 
4427
+ let hosts = ""
4428
+ let hosts_file = this.kernel.path("config/gh/hosts.yml")
4429
+ let e = await this.exists(hosts_file)
4430
+ console.log({ hosts_file, e })
4431
+ if (e) {
4432
+ hosts = await fs.promises.readFile(hosts_file, "utf8")
4433
+ console.log( { hosts: `#${hosts}#` })
4434
+ if (hosts.startsWith("{}")) {
4435
+ hosts = ""
4436
+ }
4437
+ }
4438
+ console.log("hosts", hosts)
4439
+
4440
+ let connected = (hosts.length > 0)
4441
+
4442
+
4425
4443
  let remote = null
4426
4444
  if (config["remote \"origin\""]) {
4427
4445
  remote = config["remote \"origin\""].url
@@ -4489,6 +4507,7 @@ class Server {
4489
4507
  }
4490
4508
 
4491
4509
  res.render("git", {
4510
+ connected,
4492
4511
  changes,
4493
4512
  dir,
4494
4513
  config,
@@ -4499,6 +4518,7 @@ class Server {
4499
4518
  })
4500
4519
  }))
4501
4520
  this.app.get("/dev/*", ex(async (req, res) => {
4521
+ console.log("GET /dev/*", req.params)
4502
4522
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
4503
4523
  bin: this.kernel.bin.preset("dev"),
4504
4524
  })
@@ -4507,19 +4527,19 @@ class Server {
4507
4527
  return
4508
4528
  }
4509
4529
  let platform = os.platform()
4510
- await this.kernel.plugin.init()
4530
+ // await this.kernel.plugin.init()
4511
4531
  let filepath = Util.u2p(req.params[0])
4512
- let plugin = await this.getPluginGlobal(filepath)
4532
+ // let plugin = await this.getPluginGlobal(filepath)
4513
4533
  let current_urls = await this.current_urls(req.originalUrl.slice(1))
4514
- let plugin_menu
4515
- try {
4516
- plugin_menu = plugin.menu[0].menu
4517
- } catch (e) {
4518
- plugin_menu = []
4519
- }
4534
+ // let plugin_menu
4535
+ // try {
4536
+ // plugin_menu = plugin.menu[0].menu
4537
+ // } catch (e) {
4538
+ // plugin_menu = []
4539
+ // }
4520
4540
  const result = {
4521
4541
  current_urls,
4522
- plugin_menu,
4542
+ plugin_menu: null,
4523
4543
  portal: this.portal,
4524
4544
  install: this.install,
4525
4545
  port: this.port,
@@ -4616,35 +4636,83 @@ class Server {
4616
4636
  }))
4617
4637
  this.app.get("/pinokio/dynamic_global/*", ex(async (req, res) => {
4618
4638
  let plugin = await this.getPluginGlobal("/" + req.params[0])
4619
- let html = ""
4620
- if (plugin && plugin.menu) {
4621
- let plugin_menu
4622
- try {
4623
- plugin_menu = plugin.menu[0].menu
4624
- } catch (e) {
4625
- plugin_menu = []
4626
- }
4627
- html = await new Promise((resolve, reject) => {
4628
- ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin_menu }, (err, html) => {
4629
- resolve(html)
4639
+ if (plugin) {
4640
+ let html = ""
4641
+ if (plugin && plugin.menu) {
4642
+ let plugin_menu
4643
+ try {
4644
+ plugin_menu = plugin.menu[0].menu
4645
+ } catch (e) {
4646
+ plugin_menu = []
4647
+ }
4648
+ html = await new Promise((resolve, reject) => {
4649
+ ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin_menu }, (err, html) => {
4650
+ resolve(html)
4651
+ })
4630
4652
  })
4631
- })
4653
+ }
4654
+ res.send(html)
4655
+ } else {
4656
+ res.send("")
4632
4657
  }
4633
- res.send(html)
4634
4658
  }))
4635
4659
  this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
4636
4660
  // await this.kernel.plugin.init()
4637
4661
  let plugin = await this.getPlugin(req.params.name)
4638
- let html = ""
4639
- if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
4640
- let running_dynamic = this.running_dynamic(req.params.name, plugin.menu)
4641
- plugin.menu = plugin.menu.concat(running_dynamic)
4642
- html = await new Promise((resolve, reject) => {
4643
- ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin.menu }, (err, html) => {
4644
- resolve(html)
4662
+ if (plugin) {
4663
+ let html = ""
4664
+ if (plugin && plugin.menu && Array.isArray(plugin.menu)) {
4665
+ let running_dynamic = this.running_dynamic(req.params.name, plugin.menu)
4666
+ plugin.menu = plugin.menu.concat(running_dynamic)
4667
+ html = await new Promise((resolve, reject) => {
4668
+ ejs.renderFile(path.resolve(__dirname, "views/partials/dynamic.ejs"), { dynamic: plugin.menu }, (err, html) => {
4669
+ resolve(html)
4670
+ })
4645
4671
  })
4646
- })
4672
+ }
4673
+ res.send(html)
4674
+ } else {
4675
+ res.send("")
4676
+ }
4677
+ }))
4678
+ this.app.get("/pinokio/ai/:name", ex(async (req, res) => {
4679
+ /*
4680
+ link to
4681
+ README.md
4682
+ AGENTS.md
4683
+ CLAUDE.md
4684
+ GEMINI.md
4685
+ */
4686
+ let c = this.kernel.path("api", req.params.name, "README.md")
4687
+ let readme_exists = await this.exists(c)
4688
+ let files
4689
+ if (readme_exists) {
4690
+ files = [
4691
+ "README.md",
4692
+ "AGENTS.md",
4693
+ "CLAUDE.md",
4694
+ "GEMINI.md"
4695
+ ]
4696
+ } else {
4697
+ files = [
4698
+ "AGENTS.md",
4699
+ "CLAUDE.md",
4700
+ "GEMINI.md"
4701
+ ]
4647
4702
  }
4703
+
4704
+
4705
+ let items = files.map((item) => {
4706
+ return {
4707
+ text: item,
4708
+ href: `/_api/${req.params.name}/${item}`
4709
+ }
4710
+ })
4711
+ let html = await new Promise((resolve, reject) => {
4712
+ ejs.renderFile(path.resolve(__dirname, "views/partials/ai.ejs"), { items }, (err, html) => {
4713
+ resolve(html)
4714
+ })
4715
+ })
4648
4716
  res.send(html)
4649
4717
  }))
4650
4718
  this.app.get("/pinokio/repos/:name", ex(async (req, res) => {
@@ -121,7 +121,7 @@ body.dark *::-webkit-scrollbar-thumb {
121
121
  .drawer.vertical-collapsed {
122
122
  max-height: 0;
123
123
  }
124
- .git.selected {
124
+ .blue.selected {
125
125
  border-left: 10px solid royalblue;
126
126
  }
127
127
  body.dark .dynamic.selected {
@@ -840,12 +840,6 @@ body.dark .appcanvas {
840
840
  </div>
841
841
  <div class='disk-usage' data-path="/"></div>
842
842
  </a>
843
- <a target="<%=logs%>" href="<%=logs%>" class='btn header-item frame-link' data-index="1" data-mode="refresh">
844
- <div class='tab'>
845
- <i class="fa-solid fa-clock-rotate-left"></i> Logs
846
- </div>
847
- <div class='disk-usage' data-path="/logs"></div>
848
- </a>
849
843
  <% if (profile || feed) { %>
850
844
  <div class='btn header-item frame-link revealer' data-group='.info-group'>
851
845
  <div class='tab'>
@@ -866,13 +860,18 @@ body.dark .appcanvas {
866
860
  <% } %>
867
861
  </div>
868
862
  <% } %>
869
- <div class='dynamic'>
870
- <% if (plugin_menu) { %>
871
- <%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
863
+ <div class="dynamic <%=type==='run' ? '' : 'selected'%>">
864
+ <% if (type !== "run") { %>
865
+ <div class="nested-menu">
866
+ <div class="btn header-item frame-link selected">
867
+ <div class="tab"><i class="fa-solid fa-code"></i> Dev</div>
868
+ <div class="loader"><i class="fa-solid fa-circle-notch fa-spin"></i></div>
869
+ </div>
870
+ </div>
872
871
  <% } %>
873
872
  </div>
874
873
  <% if (type === "browse") { %>
875
- <div class='nested-menu selected git'>
874
+ <div class='nested-menu selected git blue'>
876
875
  <div class='btn header-item frame-link reveal'>
877
876
  <div class='tab'><i class="fa-solid fa-cloud"></i> Git</div>
878
877
  <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
@@ -880,6 +879,22 @@ body.dark .appcanvas {
880
879
  <div class='submenu' id='git-repos'>
881
880
  </div>
882
881
  </div>
882
+ <div class='nested-menu selected blue'>
883
+ <a target="<%=logs%>" href="<%=logs%>" class='btn header-item frame-link' data-index="1" data-mode="refresh">
884
+ <div class='tab'>
885
+ <i class="fa-solid fa-clock-rotate-left"></i> Logs
886
+ </div>
887
+ <div class='disk-usage' data-path="/logs"></div>
888
+ </a>
889
+ </div>
890
+ <div class='nested-menu selected blue'>
891
+ <div class='btn header-item frame-link reveal'>
892
+ <div class='tab'><i class="fa-solid fa-robot"></i> AI Recipes</div>
893
+ <div class='loader'><i class="fa-solid fa-angle-down"></i><i class="fa-solid fa-angle-up hidden"></i></div>
894
+ </div>
895
+ <div class='submenu hidden' id='ai-prompts'>
896
+ </div>
897
+ </div>
883
898
  <% } %>
884
899
  <div class='btn header-item frame-link revealer' data-group='.config-group'>
885
900
  <div class='tab'>
@@ -915,7 +930,9 @@ body.dark .appcanvas {
915
930
  <div class='app-info'>
916
931
  <% if (type === 'run') { %>
917
932
  <div class='mode-section'>
933
+ <!--
918
934
  <h3><i class='fa-solid fa-circle-play'></i> Run mode</h3>
935
+ -->
919
936
  <div class='desc'>Switch to dev mode to make changes to the app.</div>
920
937
  <a class='btn' href="<%=home.endsWith("/") ? home : home + "/" %>dev"><i class="fa-solid fa-code"></i> Switch to Dev mode</a>
921
938
  </div>
@@ -1491,7 +1508,6 @@ body.dark .appcanvas {
1491
1508
  edit: true,
1492
1509
  redirect: (new_path) => {
1493
1510
  let u = location.href.replace(/(\/pinokio\/browser\/)[^\/]+(?=\/|$)/, `$1${new_path}`)
1494
- debugger
1495
1511
  return u
1496
1512
  }
1497
1513
  })
@@ -1510,7 +1526,6 @@ body.dark .appcanvas {
1510
1526
  edit: true,
1511
1527
  redirect: (new_path) => {
1512
1528
  let u = location.href.replace(/(\/pinokio\/browser\/)[^\/]+(?=\/|$)/, `$1${new_path}`)
1513
- debugger
1514
1529
  return u
1515
1530
  }
1516
1531
  })
@@ -1530,7 +1545,6 @@ body.dark .appcanvas {
1530
1545
  edit: true,
1531
1546
  redirect: (new_path) => {
1532
1547
  let u = location.href.replace(/(\/pinokio\/browser\/)[^\/]+(?=\/|$)/, `$1${new_path}`)
1533
- debugger
1534
1548
  return u
1535
1549
  }
1536
1550
  })
@@ -1671,7 +1685,6 @@ body.dark .appcanvas {
1671
1685
  e.stopPropagation()
1672
1686
  let shell = target.closest("[data-shell]")
1673
1687
  if (shell) {
1674
- debugger
1675
1688
  let shell_id = shell.getAttribute("data-shell")
1676
1689
  n.Noty({
1677
1690
  text: `stopping shell`,
@@ -1728,7 +1741,6 @@ body.dark .appcanvas {
1728
1741
  })
1729
1742
  } else {
1730
1743
  let link = target.closest("[href]")
1731
- debugger
1732
1744
  if (link) {
1733
1745
  let src = new URL(link.href).pathname
1734
1746
  target.querySelector("i.fa-square").className = "fa-solid fa-check"
@@ -1835,7 +1847,7 @@ body.dark .appcanvas {
1835
1847
  <% } else { %>
1836
1848
  // dev mode => already dev. reveal the hidden menu
1837
1849
  if (target.closest(".dynamic")) {
1838
- target.closest(".dynamic").classList.toggle("selected")
1850
+ target.closest(".dynamic").classList.add("selected")
1839
1851
  target.closest(".nested-menu").querySelector(".submenu").classList.toggle("hidden")
1840
1852
  target.querySelector(".loader .fa-angle-down").classList.toggle("hidden")
1841
1853
  target.querySelector(".loader .fa-angle-up").classList.toggle("hidden")
@@ -1910,8 +1922,45 @@ body.dark .appcanvas {
1910
1922
  }
1911
1923
  })
1912
1924
  }
1913
- const refresh = async (silent, options) => {
1914
1925
 
1926
+ let dynamic_loaded = false
1927
+
1928
+ const try_dynamic = async () => {
1929
+ console.log("Try_dynamic")
1930
+ let rendered
1931
+ const dynamic = await fetch("<%=dynamic%>").then((res) => {
1932
+ return res.text()
1933
+ })
1934
+ if (dynamic && dynamic.length > 0) {
1935
+ console.log({ dynamic })
1936
+ if (document.querySelector(".dynamic")) {
1937
+ document.querySelector(".dynamic").innerHTML = dynamic
1938
+ }
1939
+ rendered = true
1940
+ } else {
1941
+ rendered = false
1942
+ }
1943
+ console.log({ rendered, dynamic_loaded })
1944
+ if (rendered) {
1945
+ if (dynamic_loaded) {
1946
+ // already loaded, don't touch the UI
1947
+ } else {
1948
+ // first time being loaded
1949
+ dynamic_loaded = true
1950
+ if (document.querySelector(".dynamic .reveal")) {
1951
+ console.log("click")
1952
+
1953
+ document.querySelector(".dynamic .reveal").click()
1954
+ }
1955
+ }
1956
+ } else {
1957
+ console.log("Not yet rendered. Try again")
1958
+ setTimeout(() => {
1959
+ try_dynamic()
1960
+ }, 1000)
1961
+ }
1962
+ }
1963
+ const refresh = async (silent, options) => {
1915
1964
 
1916
1965
  if (options && options.nodelay) {
1917
1966
  } else {
@@ -1926,14 +1975,9 @@ body.dark .appcanvas {
1926
1975
  })
1927
1976
  document.querySelector(".menu").innerHTML = html
1928
1977
 
1929
- const dynamic = await fetch("<%=dynamic%>").then((res) => {
1930
- return res.text()
1931
- })
1932
- console.log({ dynamic })
1933
- if (document.querySelector(".dynamic")) {
1934
- document.querySelector(".dynamic").innerHTML = dynamic
1935
- }
1936
-
1978
+ <% if (type !== 'run') { %>
1979
+ try_dynamic()
1980
+ <% } %>
1937
1981
 
1938
1982
  const repos = await fetch("<%=repos%>").then((res) => {
1939
1983
  return res.text()
@@ -1942,6 +1986,14 @@ body.dark .appcanvas {
1942
1986
  document.querySelector("#git-repos").innerHTML = repos
1943
1987
  }
1944
1988
 
1989
+ const ai = await fetch("<%=ai%>").then((res) => {
1990
+ return res.text()
1991
+ })
1992
+ console.log({ ai })
1993
+ if (document.querySelector("#ai-prompts")) {
1994
+ document.querySelector("#ai-prompts").innerHTML = ai
1995
+ }
1996
+
1945
1997
  location.hash = ""
1946
1998
 
1947
1999
  // render the selected frame only if not silent
@@ -2053,7 +2105,6 @@ body.dark .appcanvas {
2053
2105
  refresh_du()
2054
2106
  refresh_du("logs")
2055
2107
  <% if (type !== 'run') { %>
2056
- debugger
2057
2108
  fetch("<%=repos%>").then((res) => {
2058
2109
  return res.text()
2059
2110
  }).then((repos) => {
@@ -2061,9 +2112,7 @@ body.dark .appcanvas {
2061
2112
  document.querySelector("#git-repos").innerHTML = repos
2062
2113
  }
2063
2114
  })
2064
- if (document.querySelector(".dynamic .reveal")) {
2065
- document.querySelector(".dynamic .reveal").click()
2066
- }
2115
+ refresh()
2067
2116
  <% } %>
2068
2117
  /*
2069
2118
  document.addEventListener("keydown", (e) => {
@@ -155,13 +155,17 @@ document.addEventListener("DOMContentLoaded", async () => {
155
155
  <% } %>
156
156
  <% if (!install_required) { %>
157
157
  let editor = ace.edit("editor");
158
- editor.setTheme("ace/theme/tomorrow");
159
158
  //editor.setTheme("ace/theme/github_light_default");
160
159
  //editor.setTheme("ace/theme/ambiance");
161
- //editor.setTheme("ace/theme/idle_fingers");
160
+ <% if (theme === "dark") { %>
161
+ editor.setTheme("ace/theme/idle_fingers");
162
+ <% } else { %>
163
+ editor.setTheme("ace/theme/tomorrow");
164
+ <% } %>
162
165
  //editor.setTheme("ace/theme/nord_dark");
163
166
  //editor.setTheme("ace/theme/textmate");
164
167
  editor.setOptions({
168
+ wrap: true,
165
169
  // maxLines: Infinity, // set to a large number
166
170
  minLines: 1 // set to a small number
167
171
  });
@@ -228,10 +232,12 @@ document.addEventListener("DOMContentLoaded", async () => {
228
232
  document.querySelector(".run .starting").classList.add("hidden")
229
233
  }
230
234
  stop() {
235
+ let cwd = "<%-JSON.stringify(execUrl).slice(1, -1)%>"
231
236
  this.socket.run({
232
237
  method: "kernel.api.stop",
233
238
  params: {
234
- uri: "<%=execUrl%>",
239
+ //uri: "<%=execUrl%>",
240
+ uri: cwd,
235
241
  //uri: "~" + location.pathname
236
242
  }
237
243
  }, (stream) => {
@@ -239,7 +245,8 @@ document.addEventListener("DOMContentLoaded", async () => {
239
245
  }
240
246
  save() {
241
247
  return new Promise((resolve, reject) => {
242
- let cwd = "<%=execUrl%>"
248
+ let cwd = "<%-JSON.stringify(execUrl).slice(1, -1)%>"
249
+ //let cwd = "<%=execUrl%>"
243
250
  //let cwd = "~" + location.pathname
244
251
  this.socket.close()
245
252
  this.socket.run({
@@ -269,7 +276,8 @@ document.addEventListener("DOMContentLoaded", async () => {
269
276
  this.socket.run({
270
277
  //uri: location.pathname.slice(1).replace("api/", ""),
271
278
  //uri: "~" + location.pathname,
272
- uri: "<%=execUrl%>",
279
+ uri: "<%-JSON.stringify(execUrl).slice(1, -1)%>",
280
+ //uri: "<%=execUrl%>",
273
281
  //uri: "~" + location.pathname,
274
282
  mode,
275
283
  input: query,
@@ -4,7 +4,7 @@
4
4
  <link href="/css/solid.min.css" rel="stylesheet">
5
5
  <link href="/css/regular.min.css" rel="stylesheet">
6
6
  <link href="/css/brands.min.css" rel="stylesheet">
7
- <link href="/style.css" rel="stylesheet"/>
7
+ <script src="/opener.js"></script>
8
8
  <% if (agent === "electron") { %>
9
9
  <link href="/electron.css" rel="stylesheet"/>
10
10
  <% } %>
@@ -14,10 +14,43 @@
14
14
  max-width: 800px;
15
15
  box-sizing: border-box;
16
16
  }
17
+ body {
18
+ margin: 0;
19
+ font-family: Sans-serif;
20
+ font-size: 14px;
21
+ display: flex;
22
+ flex-direction: column;
23
+ height: 100%;
24
+ }
25
+ aside {
26
+ min-width: 200px;
27
+ position: sticky;
28
+ top: 0;
29
+ }
30
+ aside .item.selected {
31
+ background: royalblue;
32
+ color: white;
33
+ }
34
+ aside .item {
35
+ display: block;
36
+ text-decoration: none;
37
+ color: royalblue;
38
+ padding: 10px;
39
+ background: rgba(0,0,0,0.1);
40
+ }
41
+ nav {
42
+ padding: 10px;
43
+ display: flex;
44
+ }
45
+ /*
17
46
  nav {
18
47
  position: sticky;
19
48
  top: 0;
20
49
  }
50
+ */
51
+ body.dark nav a.btn, nav a.btn {
52
+ background: royalblue;
53
+ }
21
54
  nav a {
22
55
  display: block;
23
56
  padding: 10px;
@@ -27,12 +60,18 @@ label {
27
60
  display: block;
28
61
  padding: 10px;
29
62
  }
63
+ body.dark nav {
64
+ background: rgba(0,0,0,0.4);
65
+ }
30
66
  nav label {
31
67
  display: block;
32
68
  padding: 10px;
33
69
  }
34
70
  main {
35
- padding: 20px;
71
+ display: flex;
72
+ overflow: auto;
73
+ scroll-behavior: smooth;
74
+ flex-grow: 1;
36
75
  }
37
76
  main iframe {
38
77
  width: 100%;
@@ -42,7 +81,16 @@ main img {
42
81
  width: 100%;
43
82
  }
44
83
  main h2 {
45
- margin: 0 0 10px;
84
+ padding: 20px 10px;
85
+ margin: 0;
86
+ font-size: 16px;
87
+ }
88
+ body.dark .changes {
89
+ color: white;
90
+ }
91
+ .changes {
92
+ height: 100%;
93
+ flex-grow: 1;
46
94
  }
47
95
  pre.l {
48
96
  /*
@@ -90,61 +138,146 @@ pre.l {
90
138
  color: #555;
91
139
  }
92
140
  .diff-block {
93
- max-height: 300px;
94
141
  background: rgba(0, 0, 0, 0.04);
95
- padding: 0 20px 20px;
96
142
  overflow: auto;
97
143
  }
144
+ body.dark h2 {
145
+ color: white;
146
+ }
147
+ body.dark main h3 {
148
+ color: white;
149
+ }
98
150
  main h3 {
99
151
  margin: 0;
100
152
  background: rgba(0, 0, 0, 0.04);
101
153
  padding: 15px;
102
- margin-top: 30px;
103
154
  font-size: 14px;
104
155
  }
156
+ body.dark .chunk {
157
+ border: 10px solid white;
158
+ }
159
+ .chunk {
160
+ border: 10px solid silver;
161
+ margin-bottom: 10px;
162
+ }
163
+ .filler {
164
+ height: 100%;
165
+ }
166
+ .chunk.selected, body.dark .chunk.selected {
167
+ border: 10px solid royalblue;
168
+ }
105
169
 
106
170
  .add { background: #e6ffed; color: green; }
107
171
  .del { background: #ffeef0; color: red; }
108
172
  .context { background: #f8f8f8; }
109
173
  .btn {
110
174
  margin-right: 10px;
175
+ cursor: pointer;
176
+ border: none;
177
+ text-decoration: none;
178
+ padding: 10px;
179
+ border-radius: 3px;
180
+ background: black;
181
+ color: white;
182
+ font-size: 12px;
183
+ display: inline-block;
184
+ }
185
+ body.dark .empty {
186
+ background: rgba(255,255,255,0.1);
187
+ }
188
+ .empty {
189
+ text-align: center;
190
+ padding: 200px 40px;
191
+ opacity: 0.5;
192
+ background: rgba(0,0,0,0.04);
111
193
  }
112
194
  </style>
113
195
  </head>
114
196
  <body class='<%=theme%>' data-platform="<%=platform%>" data-agent="<%=agent%>">
115
197
  <nav>
116
198
  <% if (remote) { %>
117
- <a href="/run/scripts/git/push.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-brands fa-github"></i> Publish to GitHub</a>
199
+ <% if (connected) { %>
200
+ <a href="/run/scripts/git/push.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-brands fa-github"></i> Publish to GitHub</a>
201
+ <% } else { %>
202
+ <a class='btn' href="/github" target="_blank">Publish to GitHub</a>
203
+ <% } %>
118
204
  <a href="/run/scripts/git/commit.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-solid fa-floppy-disk"></i> Commit (Save the current version)</a>
119
205
  <a href="<%=remote%>" target="_blank"><%=remote%></a>
120
206
  <% } else { %>
121
- <a href="/run/scripts/git/create.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-brands fa-github"></i> Create on GitHub</a>
207
+ <% if (connected) { %>
208
+ <a href="/run/scripts/git/create.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-brands fa-github"></i> Create on GitHub</a>
209
+ <% } else { %>
210
+ <a class='btn' href="/github" target="_blank">Create on GitHub</a>
211
+ <% } %>
122
212
  <a href="/run/scripts/git/commit.json?cwd=<%=encodeURIComponent(dir)%>" class='btn'><i class="fa-solid fa-floppy-disk"></i> Commit (Save the current version)</a>
123
213
  <% } %>
124
214
  </nav>
125
215
  <main>
126
- <h2>Changed Files</h2>
127
- <% if (changes && changes.length > 0) { %>
128
- <% changes.forEach(({ file, status, binary, diff, webpath }) => { %>
129
- <h3>[<%= status %>] <%= file %></h3>
130
- <% if (binary) { %>
131
- <div class="binary-notice">[Binary file <%= status %>]</div>
132
- <iframe src="<%=webpath%>"></iframe>
216
+ <aside>
217
+ <h2><i class="fa-solid fa-code-compare"></i> Changes</h2>
218
+ <% if (changes && changes.length > 0) { %>
219
+ <% changes.forEach(({ file, status, binary, diff, webpath }) => { %>
220
+ <a class='item' data-selector="<%=file%>" href="#<%=file%>"><%=file%></a>
221
+ <% }) %>
222
+ <% } %>
223
+ </aside>
224
+ <div class='changes'>
225
+ <% if (changes && changes.length > 0) { %>
226
+ <% changes.forEach(({ file, status, binary, diff, webpath }) => { %>
227
+ <div id="<%=file%>" class='chunk' data-selection="<%=file%>">
228
+ <h3>[<%= status %>] <%= file %></h3>
229
+ <% if (binary) { %>
230
+ <div class="binary-notice">[Binary file <%= status %>]</div>
231
+ <iframe src="<%=webpath%>"></iframe>
232
+ <% } else { %>
233
+ <div class="diff-block">
234
+ <% diff.forEach(({ line, lineOld, lineNew, type }) => { %>
235
+ <div class="diff-line <%= type %>">
236
+ <span class="lnum old"><%= lineOld !== '' ? lineOld : '' %></span>
237
+ <span class="lnum new"><%= lineNew !== '' ? lineNew : '' %></span>
238
+ <span class="code"><%= (type === 'add' ? '+ ' : type === 'del' ? '- ' : ' ') + line %></span>
239
+ </div>
240
+ <% }); %>
241
+ </div>
242
+ <% } %>
243
+ </div>
244
+ <% }); %>
245
+ <div class='filler'></div>
133
246
  <% } else { %>
134
- <div class="diff-block">
135
- <% diff.forEach(({ line, lineOld, lineNew, type }) => { %>
136
- <div class="diff-line <%= type %>">
137
- <span class="lnum old"><%= lineOld !== '' ? lineOld : '' %></span>
138
- <span class="lnum new"><%= lineNew !== '' ? lineNew : '' %></span>
139
- <span class="code"><%= (type === 'add' ? '+ ' : type === 'del' ? '- ' : ' ') + line %></span>
140
- </div>
141
- <% }); %>
142
- </div>
247
+ <div class='empty'>No changes</div>
143
248
  <% } %>
144
- <% }); %>
145
- <% } else { %>
146
- <div class='empty'>No changes</div>
147
- <% } %>
249
+ </div>
148
250
  </main>
251
+ <script>
252
+ document.querySelector("nav").addEventListener("click", (e) => {
253
+ let redirect = e.target.getAttribute("href")
254
+ if (redirect) {
255
+ const c = confirm("Please connect with GitHub first")
256
+ if (c) {
257
+ console.log("redirect")
258
+ } else {
259
+ e.preventDefault()
260
+ e.stopPropagation()
261
+ }
262
+ }
263
+ })
264
+ document.querySelector("aside").addEventListener("click", (e) => {
265
+ if (e.target.classList.contains("item")) {
266
+ document.querySelectorAll(".item").forEach((el) => {
267
+ el.classList.remove("selected")
268
+ })
269
+ e.target.classList.add("selected")
270
+
271
+
272
+
273
+ let selector = e.target.getAttribute("data-selector")
274
+ console.log({ selector })
275
+ document.querySelectorAll(".chunk").forEach((el) => {
276
+ el.classList.remove("selected")
277
+ })
278
+ document.querySelector(`[data-selection='${selector}']`).classList.add("selected")
279
+ }
280
+ })
281
+ </script>
149
282
  </body>
150
283
  </html>
@@ -61,8 +61,6 @@ body {
61
61
  align-items: center;
62
62
  }
63
63
  .btn {
64
- margin: 5px;
65
- width: 100px;
66
64
  text-align: center;
67
65
  padding: 10px;
68
66
  background: black;
@@ -108,7 +106,9 @@ main {
108
106
  display: flex;
109
107
  justify-content: center;
110
108
  margin: 0 auto;
109
+ /*
111
110
  max-width: 800px;
111
+ */
112
112
  width: 100%;
113
113
  align-items: center;
114
114
  }
@@ -126,20 +126,26 @@ main {
126
126
  display: block;
127
127
  margin-bottom: 5px;
128
128
  }
129
- .setup-items .gitconfig-footer .btn {
130
- margin: 0 0 5px;
129
+ .setup-items .gitconfig-footer {
130
+ text-align: center;
131
+ }
132
+ .setup-items .btn {
133
+ width: 100%;
134
+ margin-bottom: 10px;
131
135
  }
132
136
  .setup-items .gitconfig {
133
137
  flex-grow: 1;
134
- padding: 30px;
135
- background: rgba(0,0,0,0.04) !important;
136
138
  margin-bottom: 20px;
137
139
  }
138
- .setup-items pre {
140
+ body.dark .setup-items pre, body.dark .setup-items .pre {
141
+ background: rgba(255,255,255,0.04) !important;
142
+ }
143
+ .setup-items pre, .setup-items .pre {
144
+ box-sizing: border-box;
145
+ margin: 10px 0 20px;
139
146
  width: 100%;
140
- padding: 30px;
147
+ padding: 20px;
141
148
  background: rgba(0,0,0,0.04) !important;
142
- margin-bottom: 20px;
143
149
  }
144
150
  .head {
145
151
  padding: 30px;
@@ -194,7 +200,28 @@ hr {
194
200
  }
195
201
  #readme {
196
202
  flex-grow: 1;
197
- padding: 30px;
203
+ }
204
+ .cols {
205
+ max-width: 1000px;
206
+ margin: 0 auto;
207
+ display: flex;
208
+ }
209
+ body.dark .column {
210
+ background: rgba(0,0,0,0.1);
211
+ }
212
+ .column {
213
+ background: rgba(0,0,0,0.04);
214
+ margin: 10px;
215
+ width: 50%;
216
+ padding: 20px;
217
+ box-sizing: border-box;
218
+ }
219
+ .column h1 {
220
+ padding: 5px;
221
+ letter-spacing: -1px;
222
+ }
223
+ ol {
224
+ padding-inline-start: 20px;
198
225
  }
199
226
  </style>
200
227
  <script src="/hotkeys.min.js"></script>
@@ -226,38 +253,48 @@ hr {
226
253
  <div class='head'>
227
254
  <h3><i class="fa-brands fa-github"></i><br><br>Connect with Github</h3>
228
255
  </div>
229
- <div class='setup-items'>
230
- <form class='gitconfig'>
231
- <div class='field'>
232
- <label>username</label>
233
- <input id='username' type='text' value="<%=gitconfig.user && gitconfig.user.name ? gitconfig.user.name : ''%>">
256
+ <div class='cols'>
257
+ <div class='column'>
258
+ <h1>GitHub</h1>
259
+ <div class='setup-items'>
260
+ <% if (hosts.length > 0) { %>
261
+ <pre><%=hosts%></pre>
262
+ <% } else { %>
263
+ <pre>not logged in</pre>
264
+ <% } %>
234
265
  </div>
235
- <div class='field'>
236
- <label>email</label>
237
- <input id='email' type='text' value="<%=gitconfig.user && gitconfig.user.email ? gitconfig.user.email : ''%>">
266
+ <div class='setup-items'>
267
+ <% items.forEach((item) => { %>
268
+ <a class='btn' href="<%=item.url%>">
269
+ <i class="<%=item.icon%>"></i> <%=item.title%>
270
+ </a>
271
+ <% }) %>
238
272
  </div>
239
- <div class='gitconfig-footer'>
240
- <button class='btn'>Update</button>
241
- <div>gitconfig stored in ~/pinokio/gitconfig file </div>
273
+ <div class='setup-items'>
274
+ <div id='readme'><%-readme%></div>
242
275
  </div>
243
- </form>
244
- </div>
245
- <div class='setup-items'>
246
- <% if (hosts.length > 0) { %>
247
- <pre><%=hosts%></pre>
248
- <% } else { %>
249
- <pre>not logged in</pre>
250
- <% } %>
251
- </div>
252
- <div class='setup-items'>
253
- <% items.forEach((item) => { %>
254
- <a class='btn' href="<%=item.url%>">
255
- <i class="<%=item.icon%>"></i> <%=item.title%>
256
- </a>
257
- <% }) %>
258
- </div>
259
- <div class='setup-items'>
260
- <div id='readme'><%-readme%></div>
276
+ </div>
277
+ <div class='column'>
278
+ <h1>Git Config</h1>
279
+ <div class='setup-items'>
280
+ <form class='gitconfig'>
281
+ <div class='pre'>
282
+ <div class='field'>
283
+ <label>username</label>
284
+ <input id='username' type='text' value="<%=gitconfig.user && gitconfig.user.name ? gitconfig.user.name : ''%>">
285
+ </div>
286
+ <div class='field'>
287
+ <label>email</label>
288
+ <input id='email' type='text' value="<%=gitconfig.user && gitconfig.user.email ? gitconfig.user.email : ''%>">
289
+ </div>
290
+ </div>
291
+ <div class='gitconfig-footer'>
292
+ <button class='btn'><i class="fa-solid fa-check"></i> Update</button>
293
+ <div>gitconfig stored in ~/pinokio/gitconfig file </div>
294
+ </div>
295
+ </form>
296
+ </div>
297
+ </div>
261
298
  </div>
262
299
  </main>
263
300
  <script>
@@ -1454,6 +1454,7 @@ body.dark .appcanvas {
1454
1454
 
1455
1455
  })
1456
1456
  const refresh = async (silent, options) => {
1457
+ debugger
1457
1458
  console.log("REFRESH")
1458
1459
  const dynamic = await fetch("<%=dynamic%>").then((res) => {
1459
1460
  return res.text()
@@ -1537,6 +1538,7 @@ body.dark .appcanvas {
1537
1538
  if (document.querySelector(".dynamic .reveal")) {
1538
1539
  document.querySelector(".dynamic .reveal").click()
1539
1540
  }
1541
+ refresh()
1540
1542
  </script>
1541
1543
  </body>
1542
1544
  </html>
@@ -0,0 +1,5 @@
1
+ <% items.forEach((item, index) => { %>
2
+ <a target="<%=item.href%>" href="<%=item.href%>" class='btn header-item frame-link' data-index="<%=index%>" data-mode="refresh">
3
+ <div class='tab'><i class="fa-regular fa-folder-open"></i> <%=item.text%></div>
4
+ </a>
5
+ <% }) %>
@@ -25,7 +25,7 @@
25
25
  img.appicon {
26
26
  width: 40px;
27
27
  height: 40px;
28
- margin-right: 10px;
28
+ margin-right: 5px;
29
29
  padding: 5px;
30
30
  border-radius: 4px;
31
31
  background: white;
@@ -53,7 +53,7 @@ h4 {
53
53
  }
54
54
 
55
55
  h2 {
56
- font-size: 25px;
56
+ font-size: 30px;
57
57
  }
58
58
  h1 {
59
59
  letter-spacing: 0px;
@@ -145,6 +145,11 @@ body.dark .radio-group label {
145
145
  }
146
146
 
147
147
  .radio-group label span {
148
+ font-size: 16px;
149
+ /*
150
+ font-weight: lighter;
151
+ */
152
+ margin-left: 5px;
148
153
  font-weight: bold;
149
154
  flex: 1;
150
155
  }
@@ -152,6 +157,10 @@ body.dark .radio-group label {
152
157
  .radio-group label .description {
153
158
  opacity: 0.7;
154
159
  margin-left: 0.5rem;
160
+ /*
161
+ width: 350px;
162
+ text-align: right;
163
+ */
155
164
  }
156
165
 
157
166
  .checkbox-group {
@@ -184,6 +193,35 @@ body.dark .conditional-options {
184
193
  */
185
194
  display: none;
186
195
  }
196
+ #step-1 .radio-group {
197
+ display: flex;
198
+ }
199
+ #step-1 .radio-group label:hover {
200
+ color: royalblue;
201
+ }
202
+ #step-1 .radio-group label .description {
203
+ margin: 0;
204
+ }
205
+ #step-1 .radio-group label .col {
206
+ flex-grow: 1;
207
+ }
208
+ #step-1 .radio-group label {
209
+ display: flex;
210
+ padding: 30px;
211
+ text-align: left;
212
+ }
213
+ #step-1 .radio-group label i {
214
+ padding-right: 15px;
215
+ display: block;
216
+ font-size: 40px;
217
+ }
218
+ #step-1 .radio-group input[type=radio] {
219
+ display: none;
220
+ }
221
+ #step-1 .radio-group label span {
222
+ margin: 0;
223
+ font-size: 20px;
224
+ }
187
225
 
188
226
  /*
189
227
  .conditional-options .radio-group label, .conditional-options .checkbox-group label {
@@ -1051,24 +1089,30 @@ body.dark .command-fields {
1051
1089
  <form id="bootstrap-form">
1052
1090
  <!-- Step 1: New vs Clone -->
1053
1091
  <div class="step-content active" id="step-1">
1054
- <h2>Step 1: Choose Your Starting Point</h2>
1092
+ <h2>What do you want to build?</h2>
1055
1093
  <div class="radio-group">
1056
1094
  <label>
1057
- <input type="radio" name="startType" value="new" required>
1058
- <span>New Project</span>
1059
- <p class="description">Start with a fresh project</p>
1095
+ <input type="radio" name="startType" value="clone" required>
1096
+ <i class="fa-solid fa-rocket"></i>
1097
+ <div class='col'>
1098
+ <span>1-Click Launcher</span>
1099
+ <p class="description">Build a 1-click launcher for an existing app (from git)</p>
1100
+ </div>
1060
1101
  </label>
1061
1102
  <label>
1062
- <input type="radio" name="startType" value="clone" required>
1063
- <span>Clone Repository</span>
1064
- <p class="description">Clone an existing git repository</p>
1103
+ <input type="radio" name="startType" value="new" required>
1104
+ <i class="fa-solid fa-code"></i>
1105
+ <div class='col'>
1106
+ <span> New Project</span>
1107
+ <p class="description">Build a new app with a built-in 1-click launcher</p>
1108
+ </div>
1065
1109
  </label>
1066
1110
  </div>
1067
1111
  </div>
1068
1112
 
1069
1113
  <!-- Step 2: Project Type -->
1070
1114
  <div class="step-content" id="step-2">
1071
- <h2>Step 2: Select Project Type</h2>
1115
+ <h2>Select Project Type</h2>
1072
1116
 
1073
1117
  <!-- Clone Project Options -->
1074
1118
  <div id="clone-options" class="project-options">
@@ -1077,19 +1121,19 @@ body.dark .command-fields {
1077
1121
  <input type="radio" name="projectType" value="nodejs">
1078
1122
  <img class='appicon' src="/asset/prototype/system/nodejs/icon.png"/>
1079
1123
  <span>Node.js</span>
1080
- <p class="description">A launcher for a typical node.js project</p>
1124
+ <p class="description">Build a 1-click launcher for a node.js project.</p>
1081
1125
  </label>
1082
1126
  <label>
1083
1127
  <input type="radio" name="projectType" value="python">
1084
1128
  <img class='appicon' src="/asset/prototype/system/python/icon.png"/>
1085
1129
  <span>Python</span>
1086
- <p class='description'>A launcher for a typical python project)</p>
1130
+ <p class='description'>Build a 1-click launcher for a python project.</p>
1087
1131
  </label>
1088
1132
  <label>
1089
1133
  <input type="radio" name="projectType" value="empty">
1090
1134
  <img class='appicon' src="/asset/prototype/system/empty/icon.png"/>
1091
1135
  <span>Any</span>
1092
- <p class='description'>A minimal launcher you can customize on your own</p>
1136
+ <p class='description'>Build a 1-click launcher for anything else or to customize on your own.</p>
1093
1137
  </label>
1094
1138
  </div>
1095
1139