neural-loom 0.2.18 → 0.2.40

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 (203) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +0 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/cache/.previewinfo +1 -1
  5. package/.next/cache/.rscinfo +1 -1
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/diagnostics/route-bundle-stats.json +2 -2
  8. package/.next/fallback-build-manifest.json +3 -3
  9. package/.next/prerender-manifest.json +3 -32
  10. package/.next/routes-manifest.json +0 -6
  11. package/.next/server/app/_global-error/page.js +6 -7
  12. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  13. package/.next/server/app/_global-error.html +1 -1
  14. package/.next/server/app/_global-error.rsc +2 -3
  15. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -3
  17. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -2
  18. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/_not-found/page.js +6 -7
  21. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/server/app/_not-found.html +1 -1
  23. package/.next/server/app/_not-found.rsc +2 -2
  24. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  25. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  26. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  27. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  28. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  29. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  30. package/.next/server/app/api/context/aider/route.js +2 -2
  31. package/.next/server/app/api/context/aider/route.js.nft.json +1 -1
  32. package/.next/server/app/api/context/claude/route.js +2 -2
  33. package/.next/server/app/api/context/claude/route.js.nft.json +1 -1
  34. package/.next/server/app/api/context/route.js +2 -2
  35. package/.next/server/app/api/context/route.js.nft.json +1 -1
  36. package/.next/server/app/api/context/ssh/route.js +2 -2
  37. package/.next/server/app/api/context/ssh/route.js.nft.json +1 -1
  38. package/.next/server/app/api/files/route.js +2 -2
  39. package/.next/server/app/api/files/route.js.nft.json +1 -1
  40. package/.next/server/app/api/git/route.js +2 -2
  41. package/.next/server/app/api/git/route.js.nft.json +1 -1
  42. package/.next/server/app/api/recents/route.js +2 -2
  43. package/.next/server/app/api/recents/route.js.nft.json +1 -1
  44. package/.next/server/app/api/sessions/inject/route.js +2 -2
  45. package/.next/server/app/api/sessions/inject/route.js.nft.json +1 -1
  46. package/.next/server/app/api/sessions/input/route.js +2 -2
  47. package/.next/server/app/api/sessions/input/route.js.nft.json +1 -1
  48. package/.next/server/app/api/sessions/launch/route.js +2 -2
  49. package/.next/server/app/api/sessions/launch/route.js.nft.json +1 -1
  50. package/.next/server/app/api/sessions/route.js +2 -2
  51. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  52. package/.next/server/app/api/sessions/scope/route.js +2 -2
  53. package/.next/server/app/api/sessions/scope/route.js.nft.json +1 -1
  54. package/.next/server/app/api/sessions/start/route.js +2 -2
  55. package/.next/server/app/api/sessions/start/route.js.nft.json +1 -1
  56. package/.next/server/app/api/sessions/stats/route.js +2 -2
  57. package/.next/server/app/api/sessions/stats/route.js.nft.json +1 -1
  58. package/.next/server/app/api/sessions/stop/route.js +2 -2
  59. package/.next/server/app/api/sessions/stop/route.js.nft.json +1 -1
  60. package/.next/server/app/index.html +1 -1
  61. package/.next/server/app/index.rsc +4 -4
  62. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  63. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  64. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  65. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  66. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  67. package/.next/server/app/page/react-loadable-manifest.json +2 -2
  68. package/.next/server/app/page.js +6 -7
  69. package/.next/server/app/page.js.nft.json +1 -1
  70. package/.next/server/app/page_client-reference-manifest.js +1 -1
  71. package/.next/server/app-paths-manifest.json +0 -1
  72. package/.next/server/chunks/{[externals]_next_dist_0arv.vj._.js → [externals]__048y_op._.js} +2 -2
  73. package/.next/server/chunks/{[root-of-the-server]__0-lisav._.js → [root-of-the-server]__0-vx6-s._.js} +3 -3
  74. package/.next/server/chunks/{[root-of-the-server]__0-lisav._.js.map → [root-of-the-server]__0-vx6-s._.js.map} +1 -1
  75. package/.next/server/chunks/{[root-of-the-server]__0k6w___._.js → [root-of-the-server]__0656ruo._.js} +3 -3
  76. package/.next/server/chunks/{[root-of-the-server]__0k6w___._.js.map → [root-of-the-server]__0656ruo._.js.map} +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__0969ozy._.js +3 -0
  78. package/.next/server/chunks/{[root-of-the-server]__0l7c-gn._.js.map → [root-of-the-server]__0969ozy._.js.map} +1 -1
  79. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js +10 -5
  80. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js.map +1 -1
  81. package/.next/server/chunks/{[root-of-the-server]__0iqaxhx._.js → [root-of-the-server]__0d9_-7g._.js} +3 -3
  82. package/.next/server/chunks/{[root-of-the-server]__0iqaxhx._.js.map → [root-of-the-server]__0d9_-7g._.js.map} +1 -1
  83. package/.next/server/chunks/{[root-of-the-server]__0qz~w0~._.js → [root-of-the-server]__0fhv7.1._.js} +3 -3
  84. package/.next/server/chunks/{[root-of-the-server]__0qz~w0~._.js.map → [root-of-the-server]__0fhv7.1._.js.map} +1 -1
  85. package/.next/server/chunks/{[root-of-the-server]__0j8-xkl._.js → [root-of-the-server]__0i83~95._.js} +2 -2
  86. package/.next/server/chunks/{[root-of-the-server]__0j8-xkl._.js.map → [root-of-the-server]__0i83~95._.js.map} +1 -1
  87. package/.next/server/chunks/[root-of-the-server]__0kn5tap._.js +4 -0
  88. package/.next/server/chunks/{[root-of-the-server]__0ntt3om._.js.map → [root-of-the-server]__0kn5tap._.js.map} +1 -1
  89. package/.next/server/chunks/[root-of-the-server]__0n-zban._.js +3 -0
  90. package/.next/server/chunks/{[root-of-the-server]__0p~u94k._.js.map → [root-of-the-server]__0n-zban._.js.map} +1 -1
  91. package/.next/server/chunks/[root-of-the-server]__0qqnqov._.js +15 -0
  92. package/.next/server/chunks/{[root-of-the-server]__0m91xif._.js.map → [root-of-the-server]__0qqnqov._.js.map} +1 -1
  93. package/.next/server/chunks/[root-of-the-server]__0sa_gpm._.js +3 -0
  94. package/.next/server/chunks/[root-of-the-server]__0sa_gpm._.js.map +1 -0
  95. package/.next/server/chunks/[root-of-the-server]__0t_~on0._.js +15 -0
  96. package/.next/server/chunks/[root-of-the-server]__0t_~on0._.js.map +1 -0
  97. package/.next/server/chunks/[root-of-the-server]__0vp8-ka._.js +3 -0
  98. package/.next/server/chunks/{[root-of-the-server]__0qyrt6_._.js.map → [root-of-the-server]__0vp8-ka._.js.map} +1 -1
  99. package/.next/server/chunks/{[root-of-the-server]__0__9rdk._.js → [root-of-the-server]__0xr~y1f._.js} +3 -3
  100. package/.next/server/chunks/{[root-of-the-server]__0__9rdk._.js.map → [root-of-the-server]__0xr~y1f._.js.map} +1 -1
  101. package/.next/server/chunks/{[root-of-the-server]__0_2_i_q._.js → [root-of-the-server]__0zkcq9q._.js} +3 -3
  102. package/.next/server/chunks/{[root-of-the-server]__0_2_i_q._.js.map → [root-of-the-server]__0zkcq9q._.js.map} +1 -1
  103. package/.next/server/chunks/{[root-of-the-server]__0bi3d5z._.js → [root-of-the-server]__0~zauvq._.js} +3 -3
  104. package/.next/server/chunks/{[root-of-the-server]__0bi3d5z._.js.map → [root-of-the-server]__0~zauvq._.js.map} +1 -1
  105. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ev3h.z.js +1 -1
  106. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ev3h.z.js.map +1 -1
  107. package/.next/server/chunks/ssr/[root-of-the-server]__0cbmrds._.js +3 -0
  108. package/.next/server/chunks/ssr/[root-of-the-server]__0cbmrds._.js.map +1 -0
  109. package/.next/server/chunks/ssr/[root-of-the-server]__0ni-l5l._.js +3 -0
  110. package/.next/server/chunks/ssr/[root-of-the-server]__0ni-l5l._.js.map +1 -0
  111. package/.next/server/chunks/ssr/{[root-of-the-server]__089dc4i._.js → [root-of-the-server]__0tcyn68._.js} +2 -2
  112. package/.next/server/chunks/ssr/[root-of-the-server]__0tcyn68._.js.map +1 -0
  113. package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js +1 -1
  114. package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js.map +1 -1
  115. package/.next/server/chunks/ssr/[root-of-the-server]__0yj_dfr._.js +3 -0
  116. package/.next/server/chunks/ssr/[root-of-the-server]__0yj_dfr._.js.map +1 -0
  117. package/.next/server/chunks/ssr/_0t7oqy6._.js +1 -1
  118. package/.next/server/chunks/ssr/_0t7oqy6._.js.map +1 -1
  119. package/.next/server/chunks/ssr/{node_modules_next_dist_0ppctuh._.js → node_modules_next_dist_0-g.jog._.js} +2 -2
  120. package/.next/server/chunks/ssr/node_modules_next_dist_0-g.jog._.js.map +1 -0
  121. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0bj8dkg.js +4 -0
  122. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0bj8dkg.js.map +1 -0
  123. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0cvrgh7.js +4 -0
  124. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0cvrgh7.js.map +1 -0
  125. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js +4 -0
  126. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js.map +1 -0
  127. package/.next/server/middleware-build-manifest.js +3 -3
  128. package/.next/server/pages/404.html +1 -1
  129. package/.next/server/pages/500.html +1 -1
  130. package/.next/server/server-reference-manifest.js +1 -1
  131. package/.next/server/server-reference-manifest.json +1 -1
  132. package/.next/static/chunks/07-tub5vqy.fk.js +1 -0
  133. package/.next/static/chunks/0kkoif8x_kxrq.js +1 -0
  134. package/.next/static/chunks/{0pp.lp89pntpv.css → 0m-_sw1425nkp.css} +1 -1
  135. package/.next/static/chunks/0wq3_34inbzj6.js +1 -0
  136. package/.next/trace +1 -1
  137. package/.next/trace-build +1 -1
  138. package/package.json +1 -1
  139. package/public/logo.png +0 -0
  140. package/public/manifest.json +22 -0
  141. package/public/sw.js +12 -0
  142. package/src/app/api/context/aider/route.ts +6 -1
  143. package/src/app/api/files/route.ts +20 -8
  144. package/src/app/api/git/route.ts +117 -1
  145. package/src/app/api/sessions/route.ts +4 -1
  146. package/src/app/components/AiderWizard.tsx +19 -1
  147. package/src/app/components/IdeLayout.tsx +715 -87
  148. package/src/app/components/MarkdownPreview.tsx +528 -0
  149. package/src/app/layout.tsx +6 -0
  150. package/src/app/page.module.css +2 -0
  151. package/src/app/page.tsx +40 -10
  152. package/src/lib/agents/AgentRunner.ts +2 -2
  153. package/src/lib/agents/AiderRunner.ts +75 -36
  154. package/src/lib/agents/SessionManager.ts +25 -10
  155. package/src/lib/agents/SessionPersister.ts +10 -0
  156. package/src/lib/agents/ShellRunner.ts +159 -0
  157. package/.next/server/app/favicon.ico/route/app-paths-manifest.json +0 -3
  158. package/.next/server/app/favicon.ico/route/build-manifest.json +0 -9
  159. package/.next/server/app/favicon.ico/route.js +0 -7
  160. package/.next/server/app/favicon.ico/route.js.map +0 -5
  161. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  162. package/.next/server/app/favicon.ico.body +0 -0
  163. package/.next/server/app/favicon.ico.meta +0 -1
  164. package/.next/server/chunks/[externals]__1334_78._.js +0 -3
  165. package/.next/server/chunks/[externals]_next_dist_0arv.vj._.js.map +0 -1
  166. package/.next/server/chunks/[root-of-the-server]__0l7c-gn._.js +0 -3
  167. package/.next/server/chunks/[root-of-the-server]__0m91xif._.js +0 -15
  168. package/.next/server/chunks/[root-of-the-server]__0ntt3om._.js +0 -3
  169. package/.next/server/chunks/[root-of-the-server]__0p~u94k._.js +0 -3
  170. package/.next/server/chunks/[root-of-the-server]__0qyrt6_._.js +0 -3
  171. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js +0 -15
  172. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js.map +0 -1
  173. package/.next/server/chunks/[root-of-the-server]__0ylcu31._.js +0 -3
  174. package/.next/server/chunks/[root-of-the-server]__0ylcu31._.js.map +0 -1
  175. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js +0 -3
  176. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js.map +0 -1
  177. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0m.429v.js +0 -3
  178. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0m.429v.js.map +0 -1
  179. package/.next/server/chunks/ssr/[root-of-the-server]__089dc4i._.js.map +0 -1
  180. package/.next/server/chunks/ssr/[root-of-the-server]__0_7i5h0._.js +0 -3
  181. package/.next/server/chunks/ssr/[root-of-the-server]__0_7i5h0._.js.map +0 -1
  182. package/.next/server/chunks/ssr/[root-of-the-server]__0hw~y-4._.js +0 -3
  183. package/.next/server/chunks/ssr/[root-of-the-server]__0hw~y-4._.js.map +0 -1
  184. package/.next/server/chunks/ssr/[root-of-the-server]__0~y8ue.._.js +0 -3
  185. package/.next/server/chunks/ssr/[root-of-the-server]__0~y8ue.._.js.map +0 -1
  186. package/.next/server/chunks/ssr/node_modules_05l396c._.js +0 -3
  187. package/.next/server/chunks/ssr/node_modules_05l396c._.js.map +0 -1
  188. package/.next/server/chunks/ssr/node_modules_next_dist_0ppctuh._.js.map +0 -1
  189. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03-z2qq.js +0 -4
  190. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03-z2qq.js.map +0 -1
  191. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07vh7rm.js +0 -4
  192. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07vh7rm.js.map +0 -1
  193. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0qp4u6g.js +0 -4
  194. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0qp4u6g.js.map +0 -1
  195. package/.next/static/chunks/0ek0280331wie.js +0 -1
  196. package/.next/static/chunks/0jxar8nsw_py3.js +0 -1
  197. package/.next/static/chunks/0whfmx9l_25d4.js +0 -1
  198. package/.next/static/media/favicon.0x3dzn~oxb6tn.ico +0 -0
  199. package/src/app/favicon.ico +0 -0
  200. /package/.next/server/chunks/{[externals]__1334_78._.js.map → [externals]__048y_op._.js.map} +0 -0
  201. /package/.next/static/{OOK46QJncZAPJTv2oCT-X → 5uGIuH8oXybqivkeHIrqb}/_buildManifest.js +0 -0
  202. /package/.next/static/{OOK46QJncZAPJTv2oCT-X → 5uGIuH8oXybqivkeHIrqb}/_clientMiddlewareManifest.js +0 -0
  203. /package/.next/static/{OOK46QJncZAPJTv2oCT-X → 5uGIuH8oXybqivkeHIrqb}/_ssgManifest.js +0 -0
@@ -0,0 +1,528 @@
1
+ "use client";
2
+
3
+ import React, { useMemo, useState } from "react";
4
+
5
+ interface MarkdownPreviewProps {
6
+ content: string;
7
+ }
8
+
9
+ interface Block {
10
+ type: "p" | "h" | "code" | "blockquote" | "ul" | "ol" | "table" | "hr" | "empty";
11
+ level?: number;
12
+ content?: string | string[];
13
+ language?: string;
14
+ }
15
+
16
+ export default function MarkdownPreview({ content }: MarkdownPreviewProps) {
17
+ const blocks = useMemo(() => {
18
+ if (!content) return [];
19
+
20
+ const lines = content.split("\n");
21
+ const parsedBlocks: Block[] = [];
22
+ let currentBlock: Block | null = null;
23
+
24
+ for (let i = 0; i < lines.length; i++) {
25
+ const line = lines[i];
26
+
27
+ // Code block handling
28
+ if (currentBlock && currentBlock.type === "code") {
29
+ if (line.trim().startsWith("```")) {
30
+ parsedBlocks.push(currentBlock);
31
+ currentBlock = null;
32
+ } else {
33
+ (currentBlock.content as string[]).push(line);
34
+ }
35
+ continue;
36
+ }
37
+
38
+ // Table block handling
39
+ if (currentBlock && currentBlock.type === "table") {
40
+ if (line.trim().startsWith("|")) {
41
+ (currentBlock.content as string[]).push(line);
42
+ continue;
43
+ } else {
44
+ parsedBlocks.push(currentBlock);
45
+ currentBlock = null;
46
+ }
47
+ }
48
+
49
+ const trimmed = line.trim();
50
+
51
+ // Code block start
52
+ if (trimmed.startsWith("```")) {
53
+ if (currentBlock) parsedBlocks.push(currentBlock);
54
+ const lang = trimmed.substring(3).trim();
55
+ currentBlock = { type: "code", language: lang, content: [] };
56
+ continue;
57
+ }
58
+
59
+ // Horizontal rule
60
+ if (/^(\s*[-*_]\s*){3,}$/.test(trimmed)) {
61
+ if (currentBlock) parsedBlocks.push(currentBlock);
62
+ parsedBlocks.push({ type: "hr" });
63
+ currentBlock = null;
64
+ continue;
65
+ }
66
+
67
+ // Heading
68
+ const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
69
+ if (headingMatch) {
70
+ if (currentBlock) parsedBlocks.push(currentBlock);
71
+ parsedBlocks.push({ type: "h", level: headingMatch[1].length, content: headingMatch[2] });
72
+ currentBlock = null;
73
+ continue;
74
+ }
75
+
76
+ // Blockquote
77
+ const blockquoteMatch = line.match(/^>\s*(.*)$/);
78
+ if (blockquoteMatch) {
79
+ if (currentBlock) {
80
+ if (currentBlock.type === "blockquote") {
81
+ (currentBlock.content as string[]).push(blockquoteMatch[1]);
82
+ continue;
83
+ } else {
84
+ parsedBlocks.push(currentBlock);
85
+ }
86
+ }
87
+ currentBlock = { type: "blockquote", content: [blockquoteMatch[1]] };
88
+ continue;
89
+ }
90
+
91
+ // Unordered list
92
+ const ulMatch = line.match(/^([-*+])\s+(.*)$/);
93
+ if (ulMatch) {
94
+ if (currentBlock) {
95
+ if (currentBlock.type === "ul") {
96
+ (currentBlock.content as string[]).push(ulMatch[2]);
97
+ continue;
98
+ } else {
99
+ parsedBlocks.push(currentBlock);
100
+ }
101
+ }
102
+ currentBlock = { type: "ul", content: [ulMatch[2]] };
103
+ continue;
104
+ }
105
+
106
+ // Ordered list
107
+ const olMatch = line.match(/^(\d+)\.\s+(.*)$/);
108
+ if (olMatch) {
109
+ if (currentBlock) {
110
+ if (currentBlock.type === "ol") {
111
+ (currentBlock.content as string[]).push(olMatch[2]);
112
+ continue;
113
+ } else {
114
+ parsedBlocks.push(currentBlock);
115
+ }
116
+ }
117
+ currentBlock = { type: "ol", content: [olMatch[2]] };
118
+ continue;
119
+ }
120
+
121
+ // Table start
122
+ if (trimmed.startsWith("|")) {
123
+ if (currentBlock) parsedBlocks.push(currentBlock);
124
+ currentBlock = { type: "table", content: [line] };
125
+ continue;
126
+ }
127
+
128
+ // Empty line
129
+ if (trimmed === "") {
130
+ if (currentBlock) {
131
+ parsedBlocks.push(currentBlock);
132
+ currentBlock = null;
133
+ }
134
+ continue;
135
+ }
136
+
137
+ // Paragraph
138
+ if (currentBlock) {
139
+ if (currentBlock.type === "p") {
140
+ currentBlock.content = (currentBlock.content as string) + " " + trimmed;
141
+ } else {
142
+ parsedBlocks.push(currentBlock);
143
+ currentBlock = { type: "p", content: trimmed };
144
+ }
145
+ } else {
146
+ currentBlock = { type: "p", content: trimmed };
147
+ }
148
+ }
149
+
150
+ if (currentBlock) {
151
+ parsedBlocks.push(currentBlock);
152
+ }
153
+
154
+ return parsedBlocks;
155
+ }, [content]);
156
+
157
+ let keyCounter = 0;
158
+
159
+ // Safe inline regex-based React parser
160
+ function parseInline(text: string): React.ReactNode[] {
161
+ function parseSegment(str: string): React.ReactNode[] {
162
+ if (!str) return [];
163
+
164
+ const codeMatch = str.match(/`(.*?)`/);
165
+ const boldMatch = str.match(/\*\*(.*?)\*\*/);
166
+ const italicMatch = str.match(/\*(.*?)\*/);
167
+ const linkMatch = str.match(/\[(.*?)\]\((.*?)\)/);
168
+
169
+ const matches = [
170
+ codeMatch ? { index: codeMatch.index!, type: "code", match: codeMatch } : null,
171
+ boldMatch ? { index: boldMatch.index!, type: "bold", match: boldMatch } : null,
172
+ italicMatch ? { index: italicMatch.index!, type: "italic", match: italicMatch } : null,
173
+ linkMatch ? { index: linkMatch.index!, type: "link", match: linkMatch } : null,
174
+ ].filter(Boolean) as { index: number; type: string; match: RegExpMatchArray }[];
175
+
176
+ if (matches.length === 0) {
177
+ return [str];
178
+ }
179
+
180
+ matches.sort((a, b) => a.index - b.index);
181
+ const first = matches[0];
182
+
183
+ const prefix = str.substring(0, first.index);
184
+ const matchedText = first.match[0];
185
+ const suffix = str.substring(first.index + matchedText.length);
186
+
187
+ const nodes: React.ReactNode[] = [];
188
+ if (prefix) {
189
+ nodes.push(prefix);
190
+ }
191
+
192
+ const key = `inline-${first.type}-${keyCounter++}`;
193
+ if (first.type === "code") {
194
+ nodes.push(<code key={key} style={inlineCodeStyle}>{first.match[1]}</code>);
195
+ } else if (first.type === "bold") {
196
+ nodes.push(<strong key={key} style={{ fontWeight: 700, color: "var(--foreground)" }}>{parseSegment(first.match[1])}</strong>);
197
+ } else if (first.type === "italic") {
198
+ nodes.push(<em key={key} style={{ fontStyle: "italic" }}>{parseSegment(first.match[1])}</em>);
199
+ } else if (first.type === "link") {
200
+ nodes.push(
201
+ <a key={key} href={first.match[2]} target="_blank" rel="noopener noreferrer" style={linkStyle}>
202
+ {parseSegment(first.match[1])}
203
+ </a>
204
+ );
205
+ }
206
+
207
+ return [...nodes, ...parseSegment(suffix)];
208
+ }
209
+
210
+ return parseSegment(text);
211
+ }
212
+
213
+ function renderTable(rows: string[], key: string) {
214
+ const parsedRows = rows.map((row) =>
215
+ row
216
+ .split("|")
217
+ .map((c) => c.trim())
218
+ .filter((_, idx, arr) => idx > 0 && idx < arr.length - 1)
219
+ );
220
+
221
+ if (parsedRows.length === 0) return null;
222
+
223
+ const headers = parsedRows[0];
224
+ const dataRows = parsedRows.slice(1).filter((r) => !r.every((cell) => cell.startsWith("-") || cell === ""));
225
+
226
+ return (
227
+ <div key={key} style={tableContainerStyle}>
228
+ <table style={tableStyle}>
229
+ <thead>
230
+ <tr>
231
+ {headers.map((h, i) => (
232
+ <th key={i} style={tableHeaderStyle}>{parseInline(h)}</th>
233
+ ))}
234
+ </tr>
235
+ </thead>
236
+ <tbody>
237
+ {dataRows.map((row, rIdx) => (
238
+ <tr key={rIdx} style={rIdx % 2 === 0 ? tableRowStyleEven : tableRowStyleOdd}>
239
+ {row.map((cell, cIdx) => (
240
+ <td key={cIdx} style={tableCellStyle}>{parseInline(cell)}</td>
241
+ ))}
242
+ </tr>
243
+ ))}
244
+ </tbody>
245
+ </table>
246
+ </div>
247
+ );
248
+ }
249
+
250
+ return (
251
+ <div style={containerStyle}>
252
+ {blocks.map((block, idx) => {
253
+ const key = `block-${block.type}-${idx}`;
254
+
255
+ switch (block.type) {
256
+ case "h": {
257
+ const headingContent = parseInline(block.content as string);
258
+ switch (block.level) {
259
+ case 1:
260
+ return <h1 key={key} style={h1Style}>{headingContent}</h1>;
261
+ case 2:
262
+ return <h2 key={key} style={h2Style}>{headingContent}</h2>;
263
+ case 3:
264
+ return <h3 key={key} style={h3Style}>{headingContent}</h3>;
265
+ default:
266
+ return <h4 key={key} style={h4Style}>{headingContent}</h4>;
267
+ }
268
+ }
269
+ case "code": {
270
+ const rawCode = (block.content as string[]).join("\n");
271
+ return <CodeBlock key={key} code={rawCode} language={block.language || ""} />;
272
+ }
273
+ case "blockquote": {
274
+ return (
275
+ <blockquote key={key} style={blockquoteStyle}>
276
+ {block.content && (block.content as string[]).map((line, lIdx) => (
277
+ <p key={lIdx} style={{ margin: "0.25rem 0" }}>{parseInline(line)}</p>
278
+ ))}
279
+ </blockquote>
280
+ );
281
+ }
282
+ case "ul": {
283
+ return (
284
+ <ul key={key} style={ulStyle}>
285
+ {block.content && (block.content as string[]).map((line, lIdx) => (
286
+ <li key={lIdx} style={liStyle}>
287
+ <span style={{ marginRight: "0.5rem", color: "var(--accent)" }}>•</span>
288
+ <div style={{ flex: 1 }}>{parseInline(line)}</div>
289
+ </li>
290
+ ))}
291
+ </ul>
292
+ );
293
+ }
294
+ case "ol": {
295
+ return (
296
+ <ol key={key} style={olStyle}>
297
+ {block.content && (block.content as string[]).map((line, lIdx) => (
298
+ <li key={lIdx} style={liStyle}>
299
+ <span style={{ marginRight: "0.5rem", color: "var(--accent)", fontWeight: 600 }}>{lIdx + 1}.</span>
300
+ <div style={{ flex: 1 }}>{parseInline(line)}</div>
301
+ </li>
302
+ ))}
303
+ </ol>
304
+ );
305
+ }
306
+ case "table": {
307
+ return renderTable(block.content as string[], key);
308
+ }
309
+ case "hr": {
310
+ return <hr key={key} style={hrStyle} />;
311
+ }
312
+ case "p": {
313
+ return <p key={key} style={pStyle}>{parseInline(block.content as string)}</p>;
314
+ }
315
+ default:
316
+ return null;
317
+ }
318
+ })}
319
+ </div>
320
+ );
321
+ }
322
+
323
+ function CodeBlock({ code, language }: { code: string; language: string }) {
324
+ const [copied, setCopied] = useState(false);
325
+ const handleCopy = () => {
326
+ navigator.clipboard.writeText(code);
327
+ setCopied(true);
328
+ setTimeout(() => setCopied(false), 2000);
329
+ };
330
+ return (
331
+ <div style={codeBlockContainerStyle}>
332
+ <div style={codeBlockHeaderStyle}>
333
+ <span>{language || "code"}</span>
334
+ <button onClick={handleCopy} style={copyBtnStyle}>
335
+ {copied ? "Copied! ✓" : "Copy"}
336
+ </button>
337
+ </div>
338
+ <pre style={preStyle}><code>{code}</code></pre>
339
+ </div>
340
+ );
341
+ }
342
+
343
+ // Styling Constants for Preview Document (Vanilla dark elements matching NeuralLoom layout)
344
+ const containerStyle: React.CSSProperties = {
345
+ fontFamily: "var(--font-sans), sans-serif",
346
+ color: "var(--foreground)",
347
+ lineHeight: 1.6,
348
+ padding: "1.5rem",
349
+ overflowY: "auto",
350
+ height: "100%",
351
+ backgroundColor: "#080c14",
352
+ borderLeft: "1px solid var(--border)",
353
+ };
354
+
355
+ const pStyle: React.CSSProperties = {
356
+ fontSize: "0.875rem",
357
+ marginBottom: "1rem",
358
+ color: "var(--foreground-muted, #94a3b8)",
359
+ };
360
+
361
+ const h1Style: React.CSSProperties = {
362
+ fontSize: "1.5rem",
363
+ fontWeight: 700,
364
+ margin: "1.5rem 0 1rem 0",
365
+ paddingBottom: "0.3rem",
366
+ borderBottom: "1px solid var(--border)",
367
+ color: "var(--foreground)",
368
+ background: "linear-gradient(135deg, #fff 0%, #a855f7 100%)",
369
+ WebkitBackgroundClip: "text",
370
+ WebkitTextFillColor: "transparent",
371
+ };
372
+
373
+ const h2Style: React.CSSProperties = {
374
+ fontSize: "1.25rem",
375
+ fontWeight: 650,
376
+ margin: "1.25rem 0 0.85rem 0",
377
+ paddingBottom: "0.25rem",
378
+ borderBottom: "1px solid var(--border)",
379
+ color: "var(--foreground)",
380
+ };
381
+
382
+ const h3Style: React.CSSProperties = {
383
+ fontSize: "1.05rem",
384
+ fontWeight: 600,
385
+ margin: "1rem 0 0.75rem 0",
386
+ color: "var(--foreground)",
387
+ };
388
+
389
+ const h4Style: React.CSSProperties = {
390
+ fontSize: "0.925rem",
391
+ fontWeight: 600,
392
+ margin: "0.85rem 0 0.5rem 0",
393
+ color: "var(--foreground)",
394
+ };
395
+
396
+ const ulStyle: React.CSSProperties = {
397
+ listStyleType: "none",
398
+ padding: 0,
399
+ margin: "0 0 1rem 0",
400
+ };
401
+
402
+ const olStyle: React.CSSProperties = {
403
+ listStyleType: "none",
404
+ padding: 0,
405
+ margin: "0 0 1rem 0",
406
+ };
407
+
408
+ const liStyle: React.CSSProperties = {
409
+ display: "flex",
410
+ alignItems: "flex-start",
411
+ fontSize: "0.875rem",
412
+ marginBottom: "0.375rem",
413
+ color: "var(--foreground-muted, #94a3b8)",
414
+ };
415
+
416
+ const hrStyle: React.CSSProperties = {
417
+ border: "none",
418
+ height: "1px",
419
+ background: "linear-gradient(90deg, var(--border) 0%, rgba(255,255,255,0.05) 100%)",
420
+ margin: "1.5rem 0",
421
+ };
422
+
423
+ // Reuse code block styles
424
+ const inlineCodeStyle: React.CSSProperties = {
425
+ fontFamily: "var(--font-mono), monospace",
426
+ fontSize: "0.85em",
427
+ backgroundColor: "rgba(255, 255, 255, 0.06)",
428
+ border: "1px solid rgba(255, 255, 255, 0.08)",
429
+ borderRadius: "4px",
430
+ padding: "0.1rem 0.3rem",
431
+ color: "#f87171",
432
+ };
433
+
434
+ const linkStyle: React.CSSProperties = {
435
+ color: "#60a5fa",
436
+ textDecoration: "underline",
437
+ fontWeight: 500,
438
+ };
439
+
440
+ const tableContainerStyle: React.CSSProperties = {
441
+ width: "100%",
442
+ overflowX: "auto",
443
+ margin: "1rem 0",
444
+ borderRadius: "8px",
445
+ border: "1px solid var(--border)",
446
+ backgroundColor: "rgba(255, 255, 255, 0.01)",
447
+ };
448
+
449
+ const tableStyle: React.CSSProperties = {
450
+ width: "100%",
451
+ borderCollapse: "collapse",
452
+ fontSize: "0.85rem",
453
+ };
454
+
455
+ const tableHeaderStyle: React.CSSProperties = {
456
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
457
+ borderBottom: "2px solid var(--border)",
458
+ padding: "0.5rem 0.75rem",
459
+ fontWeight: 700,
460
+ textAlign: "left",
461
+ color: "var(--foreground)",
462
+ };
463
+
464
+ const tableCellStyle: React.CSSProperties = {
465
+ borderBottom: "1px solid var(--border)",
466
+ padding: "0.5rem 0.75rem",
467
+ color: "var(--foreground-muted, #94a3b8)",
468
+ };
469
+
470
+ const tableRowStyleEven: React.CSSProperties = {
471
+ backgroundColor: "rgba(255, 255, 255, 0.01)",
472
+ };
473
+
474
+ const tableRowStyleOdd: React.CSSProperties = {
475
+ backgroundColor: "transparent",
476
+ };
477
+
478
+ const blockquoteStyle: React.CSSProperties = {
479
+ borderLeft: "4px solid #a855f7",
480
+ backgroundColor: "rgba(168, 85, 247, 0.03)",
481
+ padding: "0.5rem 1rem",
482
+ margin: "1rem 0",
483
+ borderRadius: "4px",
484
+ fontStyle: "italic",
485
+ color: "var(--muted)",
486
+ };
487
+
488
+ const codeBlockContainerStyle: React.CSSProperties = {
489
+ margin: "1rem 0",
490
+ borderRadius: "8px",
491
+ border: "1px solid var(--border)",
492
+ backgroundColor: "#05070a",
493
+ overflow: "hidden",
494
+ };
495
+
496
+ const codeBlockHeaderStyle: React.CSSProperties = {
497
+ display: "flex",
498
+ justifyContent: "space-between",
499
+ alignItems: "center",
500
+ padding: "0.35rem 0.75rem",
501
+ backgroundColor: "rgba(255, 255, 255, 0.02)",
502
+ borderBottom: "1px solid var(--border)",
503
+ fontSize: "0.7rem",
504
+ color: "var(--muted)",
505
+ textTransform: "uppercase",
506
+ fontFamily: "var(--font-mono), monospace",
507
+ };
508
+
509
+ const preStyle: React.CSSProperties = {
510
+ margin: 0,
511
+ padding: "1rem",
512
+ overflowX: "auto",
513
+ fontSize: "0.8rem",
514
+ fontFamily: "var(--font-mono), monospace",
515
+ lineHeight: 1.5,
516
+ color: "#e2e8f0",
517
+ };
518
+
519
+ const copyBtnStyle: React.CSSProperties = {
520
+ background: "none",
521
+ border: "1px solid var(--border)",
522
+ borderRadius: "4px",
523
+ padding: "2px 6px",
524
+ color: "var(--muted)",
525
+ cursor: "pointer",
526
+ fontSize: "0.65rem",
527
+ transition: "all 0.1s ease",
528
+ };
@@ -15,6 +15,12 @@ const geistMono = Geist_Mono({
15
15
  export const metadata: Metadata = {
16
16
  title: "NeuralLoom - Unified AI Agent Orchestrator & Context Bridge",
17
17
  description: "Advanced console dashboard and context staging manager for Claude Code and simulated AI agents.",
18
+ icons: {
19
+ icon: "/logo.png",
20
+ shortcut: "/logo.png",
21
+ apple: "/logo.png",
22
+ },
23
+ manifest: "/manifest.json",
18
24
  };
19
25
 
20
26
  export default function RootLayout({
@@ -335,5 +335,7 @@
335
335
  padding: 1rem 1.5rem;
336
336
  gap: 1rem;
337
337
  overflow: hidden;
338
+ max-width: none;
339
+ width: 100%;
338
340
  }
339
341
 
package/src/app/page.tsx CHANGED
@@ -89,6 +89,7 @@ export default function Home() {
89
89
  const [isClaudeDockerLaunch, setIsClaudeDockerLaunch] = useState<boolean>(false);
90
90
  const [serverCwd, setServerCwd] = useState<string>("");
91
91
  const [wsPort, setWsPort] = useState<number>(3001);
92
+ const [appVersion, setAppVersion] = useState<string>("0.2.19");
92
93
  const [lastSessionConfig, setLastSessionConfig] = useState<{
93
94
  type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
94
95
  name?: string;
@@ -96,9 +97,10 @@ export default function Home() {
96
97
  scopedDirs?: string[];
97
98
  } | null>(null);
98
99
 
100
+ const activeAgents = sessions.filter(s => s.id !== "workspace-terminal");
99
101
  const activeSession = sessions.find(s => s.id === activeSessionId);
100
- const isDockerActive = sessions.some(s => (s.type === 'claude-docker' || s.type === 'aider-docker') && s.status === 'running');
101
- const isSshActive = sessions.some(s => s.type === 'claude-ssh' && s.status === 'running');
102
+ const isDockerActive = activeAgents.some(s => (s.type === 'claude-docker' || s.type === 'aider-docker') && s.status === 'running');
103
+ const isSshActive = activeAgents.some(s => s.type === 'claude-ssh' && s.status === 'running');
102
104
 
103
105
  // Fetch all sessions on mount & periodically
104
106
  const fetchSessions = async () => {
@@ -114,6 +116,9 @@ export default function Home() {
114
116
  if (data.originalCwd) {
115
117
  setServerCwd(data.originalCwd);
116
118
  }
119
+ if (data.version) {
120
+ setAppVersion(data.version);
121
+ }
117
122
  } catch (err) {
118
123
  console.error("Failed to fetch sessions:", err);
119
124
  }
@@ -139,6 +144,14 @@ export default function Home() {
139
144
  await fetchRecents();
140
145
  };
141
146
  initFetch();
147
+
148
+ // Register PWA Service Worker for standalone taskbar icon installation
149
+ if ('serviceWorker' in navigator) {
150
+ navigator.serviceWorker.register('/sw.js')
151
+ .then((reg) => console.log('Service Worker registered:', reg.scope))
152
+ .catch((err) => console.error('Service Worker registration failed:', err));
153
+ }
154
+
142
155
  const interval = setInterval(fetchSessions, 4000);
143
156
  return () => clearInterval(interval);
144
157
  }, []);
@@ -231,9 +244,12 @@ export default function Home() {
231
244
  <div className={`${styles.container} ${activeSession ? styles.containerActive : ""}`}>
232
245
  {/* Header */}
233
246
  <header className={styles.header}>
234
- <div className={styles.logoArea}>
235
- <h1 className={styles.title}>NeuralLoom</h1>
236
- <span className={styles.subtitle}>Unified AI Orchestrator & Context Bridge</span>
247
+ <div style={{ display: "flex", alignItems: "center", gap: "1.5rem" }}>
248
+ <img src="/logo.png" alt="NeuralLoom Logo" style={{ width: "160px", height: "160px", borderRadius: "16px", objectFit: "contain" }} />
249
+ <div className={styles.logoArea}>
250
+ <h1 className={styles.title} style={{ margin: 0, lineHeight: 1.1 }}>NeuralLoom</h1>
251
+ <span className={styles.subtitle}>Unified AI Orchestrator & Context Bridge</span>
252
+ </div>
237
253
  </div>
238
254
  <div className={styles.statusIndicator}>
239
255
  <span className={styles.pulseDot}></span>
@@ -263,7 +279,7 @@ export default function Home() {
263
279
  <section>
264
280
  <h2 className={styles.sectionTitle}>Active AI Agents</h2>
265
281
 
266
- {sessions.length === 0 ? (
282
+ {activeAgents.length === 0 ? (
267
283
  <div className="card" style={{ padding: "3rem 1.5rem", textAlign: "center" }}>
268
284
  <p style={{ color: "var(--muted)", marginBottom: "1.5rem" }}>
269
285
  No active agent sessions in this workspace. Launch a Claude or Aider agent session to begin.
@@ -334,7 +350,7 @@ export default function Home() {
334
350
  </div>
335
351
  ) : (
336
352
  <div className={styles.agentGrid}>
337
- {sessions.map(session => (
353
+ {activeAgents.map(session => (
338
354
  <div key={session.id} className={`${styles.agentCard} card ${activeSessionId === session.id ? styles.activeBorder : ""}`}>
339
355
  <div className={styles.agentHeader}>
340
356
  <span className={styles.agentName}>{session.name}</span>
@@ -410,7 +426,7 @@ export default function Home() {
410
426
  </section>
411
427
 
412
428
  {/* Interactive Session Terminal Display */}
413
- {activeSessionId && (
429
+ {activeSessionId && activeSessionId !== "workspace-terminal" && (
414
430
  <section className="card" style={{ display: "flex", flexDirection: "column", border: "1px solid var(--border-focus)" }}>
415
431
  <div className={styles.agentHeader} style={{ justifyContent: "space-between", background: "rgba(109, 40, 217, 0.08)" }}>
416
432
  <span style={{ fontWeight: 600, display: "flex", alignItems: "center", gap: "0.5rem" }}>
@@ -469,8 +485,22 @@ export default function Home() {
469
485
  </section>
470
486
  )}
471
487
 
472
- {/* Context Hub Staging section */}
488
+ {/* Workspace Shell Terminal */}
473
489
  <section>
490
+ <h2 className={styles.sectionTitle}>Workspace Shell Terminal</h2>
491
+ <div className="card" style={{ display: "flex", flexDirection: "column", border: "1px solid var(--border)", padding: 0 }}>
492
+ <div className={styles.agentHeader} style={{ justifyContent: "space-between", background: "rgba(255, 255, 255, 0.02)" }}>
493
+ <span style={{ fontWeight: 600, display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "0.85rem" }}>
494
+ 💻 Local Workspace Shell (CWD: {serverCwd || "root"})
495
+ </span>
496
+ <span style={{ fontSize: "0.7rem", color: "var(--muted)" }}>Pre-configured environment</span>
497
+ </div>
498
+ <TerminalConsole sessionId="workspace-terminal" height="300px" wsPort={wsPort} />
499
+ </div>
500
+ </section>
501
+
502
+ {/* Context Hub Staging section */}
503
+ <section style={{ marginTop: "2rem" }}>
474
504
  <h2 className={styles.sectionTitle}>Shared Context Hub & Slash Commands</h2>
475
505
  <div className={styles.contextList}>
476
506
  <div className={`${styles.contextItem} card`}>
@@ -671,7 +701,7 @@ export default function Home() {
671
701
 
672
702
  {/* Footer */}
673
703
  <footer className={styles.footer}>
674
- <span>NeuralLoom v0.2.13 — Experimental AI Orchestrator Bridge</span>
704
+ <span>NeuralLoom v{appVersion} — Experimental AI Orchestrator Bridge</span>
675
705
  <span>Directory: {serverCwd || "Loading..."}</span>
676
706
  </footer>
677
707
 
@@ -1,7 +1,7 @@
1
1
  export interface RunnerStatus {
2
2
  id: string;
3
3
  name: string;
4
- type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
4
+ type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker' | 'shell';
5
5
  status: 'idle' | 'running' | 'stopped';
6
6
  createdAt: string;
7
7
  logsCount: number;
@@ -14,7 +14,7 @@ export interface RunnerStatus {
14
14
  export abstract class AgentRunner {
15
15
  abstract id: string;
16
16
  abstract name: string;
17
- abstract type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
17
+ abstract type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker' | 'shell';
18
18
  abstract status: 'idle' | 'running' | 'stopped';
19
19
  abstract createdAt: string;
20
20
  abstract logs: string[];