neural-loom 0.2.19 → 0.2.41

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 (226) hide show
  1. package/.next/build-manifest.json +3 -3
  2. package/.next/cache/.previewinfo +1 -1
  3. package/.next/cache/.rscinfo +1 -1
  4. package/.next/cache/.tsbuildinfo +1 -1
  5. package/.next/diagnostics/build-diagnostics.json +1 -1
  6. package/.next/fallback-build-manifest.json +3 -3
  7. package/.next/server/app/_global-error/page.js +6 -7
  8. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/server/app/_not-found/page.js +6 -7
  10. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  11. package/.next/server/app/api/context/aider/route.js +2 -2
  12. package/.next/server/app/api/context/aider/route.js.nft.json +1 -1
  13. package/.next/server/app/api/context/claude/route.js +2 -2
  14. package/.next/server/app/api/context/claude/route.js.nft.json +1 -1
  15. package/.next/server/app/api/context/route.js +2 -2
  16. package/.next/server/app/api/context/route.js.nft.json +1 -1
  17. package/.next/server/app/api/context/ssh/route.js +2 -2
  18. package/.next/server/app/api/context/ssh/route.js.nft.json +1 -1
  19. package/.next/server/app/api/files/route.js +2 -2
  20. package/.next/server/app/api/files/route.js.nft.json +1 -1
  21. package/.next/server/app/api/git/route.js +2 -2
  22. package/.next/server/app/api/git/route.js.nft.json +1 -1
  23. package/.next/server/app/api/recents/route.js +2 -2
  24. package/.next/server/app/api/recents/route.js.nft.json +1 -1
  25. package/.next/server/app/api/sessions/inject/route.js +2 -2
  26. package/.next/server/app/api/sessions/inject/route.js.nft.json +1 -1
  27. package/.next/server/app/api/sessions/input/route.js +2 -2
  28. package/.next/server/app/api/sessions/input/route.js.nft.json +1 -1
  29. package/.next/server/app/api/sessions/launch/route.js +2 -2
  30. package/.next/server/app/api/sessions/launch/route.js.nft.json +1 -1
  31. package/.next/server/app/api/sessions/route.js +2 -2
  32. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  33. package/.next/server/app/api/sessions/scope/route.js +2 -2
  34. package/.next/server/app/api/sessions/scope/route.js.nft.json +1 -1
  35. package/.next/server/app/api/sessions/start/route.js +2 -2
  36. package/.next/server/app/api/sessions/start/route.js.nft.json +1 -1
  37. package/.next/server/app/api/sessions/stats/route.js +2 -2
  38. package/.next/server/app/api/sessions/stats/route.js.nft.json +1 -1
  39. package/.next/server/app/api/sessions/stop/route.js +2 -2
  40. package/.next/server/app/api/sessions/stop/route.js.nft.json +1 -1
  41. package/.next/server/app/page/react-loadable-manifest.json +4 -4
  42. package/.next/server/app/page.js +6 -7
  43. package/.next/server/app/page.js.nft.json +1 -1
  44. package/.next/server/app/page_client-reference-manifest.js +1 -1
  45. package/.next/server/app-paths-manifest.json +0 -1
  46. package/.next/server/chunks/{[externals]_next_dist_0arv.vj._.js → [externals]__048y_op._.js} +2 -2
  47. package/.next/server/chunks/[root-of-the-server]__005xmeg._.js +15 -0
  48. package/.next/server/chunks/{[root-of-the-server]__0iqaxhx._.js.map → [root-of-the-server]__005xmeg._.js.map} +1 -1
  49. package/.next/server/chunks/{[root-of-the-server]__0k6w___._.js → [root-of-the-server]__0656ruo._.js} +3 -3
  50. package/.next/server/chunks/{[root-of-the-server]__0k6w___._.js.map → [root-of-the-server]__0656ruo._.js.map} +1 -1
  51. package/.next/server/chunks/[root-of-the-server]__0969ozy._.js +3 -0
  52. package/.next/server/chunks/{[root-of-the-server]__0l7c-gn._.js.map → [root-of-the-server]__0969ozy._.js.map} +1 -1
  53. package/.next/server/chunks/[root-of-the-server]__0_~zjnf._.js +15 -0
  54. package/.next/server/chunks/{[root-of-the-server]__0qz~w0~._.js.map → [root-of-the-server]__0_~zjnf._.js.map} +1 -1
  55. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js +9 -9
  56. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js.map +1 -1
  57. package/.next/server/chunks/[root-of-the-server]__0hl8jxx._.js +15 -0
  58. package/.next/server/chunks/{[root-of-the-server]__0bi3d5z._.js.map → [root-of-the-server]__0hl8jxx._.js.map} +1 -1
  59. package/.next/server/chunks/[root-of-the-server]__0ho7s6e._.js +15 -0
  60. package/.next/server/chunks/{[root-of-the-server]__0-lisav._.js.map → [root-of-the-server]__0ho7s6e._.js.map} +1 -1
  61. package/.next/server/chunks/{[root-of-the-server]__0j8-xkl._.js → [root-of-the-server]__0i83~95._.js} +2 -2
  62. package/.next/server/chunks/{[root-of-the-server]__0j8-xkl._.js.map → [root-of-the-server]__0i83~95._.js.map} +1 -1
  63. package/.next/server/chunks/[root-of-the-server]__0kn5tap._.js +4 -0
  64. package/.next/server/chunks/{[root-of-the-server]__0ntt3om._.js.map → [root-of-the-server]__0kn5tap._.js.map} +1 -1
  65. package/.next/server/chunks/[root-of-the-server]__0lh_jn-._.js +15 -0
  66. package/.next/server/chunks/{[root-of-the-server]__0__9rdk._.js.map → [root-of-the-server]__0lh_jn-._.js.map} +1 -1
  67. package/.next/server/chunks/[root-of-the-server]__0mq3k0u._.js +15 -0
  68. package/.next/server/chunks/{[root-of-the-server]__0_2_i_q._.js.map → [root-of-the-server]__0mq3k0u._.js.map} +1 -1
  69. package/.next/server/chunks/[root-of-the-server]__0n-zban._.js +3 -0
  70. package/.next/server/chunks/{[root-of-the-server]__0p~u94k._.js.map → [root-of-the-server]__0n-zban._.js.map} +1 -1
  71. package/.next/server/chunks/[root-of-the-server]__0rr36fm._.js +15 -0
  72. package/.next/server/chunks/{[root-of-the-server]__0m91xif._.js.map → [root-of-the-server]__0rr36fm._.js.map} +1 -1
  73. package/.next/server/chunks/[root-of-the-server]__0sa_gpm._.js +3 -0
  74. package/.next/server/chunks/[root-of-the-server]__0sa_gpm._.js.map +1 -0
  75. package/.next/server/chunks/[root-of-the-server]__0vp8-ka._.js +3 -0
  76. package/.next/server/chunks/{[root-of-the-server]__0qyrt6_._.js.map → [root-of-the-server]__0vp8-ka._.js.map} +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__0xcrc-1._.js +15 -0
  78. package/.next/server/chunks/[root-of-the-server]__0xcrc-1._.js.map +1 -0
  79. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ev3h.z.js +1 -1
  80. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ev3h.z.js.map +1 -1
  81. package/.next/server/chunks/ssr/[root-of-the-server]__0cbmrds._.js +3 -0
  82. package/.next/server/chunks/ssr/[root-of-the-server]__0cbmrds._.js.map +1 -0
  83. package/.next/server/chunks/ssr/[root-of-the-server]__0ni-l5l._.js +3 -0
  84. package/.next/server/chunks/ssr/[root-of-the-server]__0ni-l5l._.js.map +1 -0
  85. package/.next/server/chunks/ssr/{[root-of-the-server]__089dc4i._.js → [root-of-the-server]__0tcyn68._.js} +2 -2
  86. package/.next/server/chunks/ssr/[root-of-the-server]__0tcyn68._.js.map +1 -0
  87. package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js +1 -1
  88. package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js.map +1 -1
  89. package/.next/server/chunks/ssr/[root-of-the-server]__0yj_dfr._.js +3 -0
  90. package/.next/server/chunks/ssr/[root-of-the-server]__0yj_dfr._.js.map +1 -0
  91. package/.next/server/chunks/ssr/_0t7oqy6._.js +1 -1
  92. package/.next/server/chunks/ssr/_0t7oqy6._.js.map +1 -1
  93. package/.next/server/chunks/ssr/{node_modules_next_dist_0ppctuh._.js → node_modules_next_dist_0-g.jog._.js} +2 -2
  94. package/.next/server/chunks/ssr/node_modules_next_dist_0-g.jog._.js.map +1 -0
  95. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0bj8dkg.js +4 -0
  96. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0bj8dkg.js.map +1 -0
  97. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0cvrgh7.js +4 -0
  98. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0cvrgh7.js.map +1 -0
  99. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js +4 -0
  100. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js.map +1 -0
  101. package/.next/server/middleware-build-manifest.js +3 -3
  102. package/.next/server/pages-manifest.json +1 -4
  103. package/.next/server/server-reference-manifest.js +1 -1
  104. package/.next/server/server-reference-manifest.json +1 -1
  105. package/.next/static/ZtSBLhTiGqKpNetcymWiC/_ssgManifest.js +1 -0
  106. package/.next/static/chunks/{0x__nd4avi0fg.js → 070hz362~_h8n.js} +1 -1
  107. package/.next/static/chunks/0bclk7hf35ac4.js +1 -0
  108. package/.next/static/chunks/0kkoif8x_kxrq.js +1 -0
  109. package/.next/static/chunks/{0pp.lp89pntpv.css → 0m-_sw1425nkp.css} +1 -1
  110. package/.next/static/chunks/17jifiroq0_q..js +1 -0
  111. package/package.json +1 -1
  112. package/public/logo.png +0 -0
  113. package/public/manifest.json +22 -0
  114. package/public/sw.js +12 -0
  115. package/src/app/api/context/aider/route.ts +6 -1
  116. package/src/app/api/files/route.ts +20 -8
  117. package/src/app/api/git/route.ts +117 -1
  118. package/src/app/api/sessions/route.ts +4 -1
  119. package/src/app/components/AiderWizard.tsx +19 -1
  120. package/src/app/components/IdeLayout.tsx +1215 -112
  121. package/src/app/components/MarkdownPreview.tsx +528 -0
  122. package/src/app/components/TerminalConsole.tsx +2 -2
  123. package/src/app/layout.tsx +5 -0
  124. package/src/app/page.module.css +2 -0
  125. package/src/app/page.tsx +42 -19
  126. package/src/lib/agents/AiderRunner.ts +83 -44
  127. package/src/lib/agents/ClaudeRunner.ts +43 -18
  128. package/src/lib/agents/DockerRunner.ts +1 -3
  129. package/src/lib/agents/SSHRunner.ts +1 -3
  130. package/src/lib/agents/SessionManager.ts +18 -9
  131. package/src/lib/agents/SessionPersister.ts +10 -0
  132. package/src/lib/agents/ShellRunner.ts +1 -3
  133. package/src/lib/agents/WebSocketServer.ts +20 -13
  134. package/.next/BUILD_ID +0 -1
  135. package/.next/app-path-routes-manifest.json +0 -21
  136. package/.next/diagnostics/route-bundle-stats.json +0 -25
  137. package/.next/export-marker.json +0 -6
  138. package/.next/images-manifest.json +0 -68
  139. package/.next/prerender-manifest.json +0 -114
  140. package/.next/required-server-files.js +0 -335
  141. package/.next/required-server-files.json +0 -335
  142. package/.next/routes-manifest.json +0 -159
  143. package/.next/server/app/_global-error.html +0 -1
  144. package/.next/server/app/_global-error.meta +0 -15
  145. package/.next/server/app/_global-error.rsc +0 -15
  146. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  147. package/.next/server/app/_global-error.segments/_full.segment.rsc +0 -15
  148. package/.next/server/app/_global-error.segments/_head.segment.rsc +0 -6
  149. package/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
  150. package/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  151. package/.next/server/app/_not-found.html +0 -1
  152. package/.next/server/app/_not-found.meta +0 -16
  153. package/.next/server/app/_not-found.rsc +0 -16
  154. package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -16
  155. package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  156. package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  157. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  158. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  159. package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  160. package/.next/server/app/favicon.ico/route/app-paths-manifest.json +0 -3
  161. package/.next/server/app/favicon.ico/route/build-manifest.json +0 -9
  162. package/.next/server/app/favicon.ico/route.js +0 -7
  163. package/.next/server/app/favicon.ico/route.js.map +0 -5
  164. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  165. package/.next/server/app/favicon.ico.body +0 -0
  166. package/.next/server/app/favicon.ico.meta +0 -1
  167. package/.next/server/app/index.html +0 -1
  168. package/.next/server/app/index.meta +0 -14
  169. package/.next/server/app/index.rsc +0 -21
  170. package/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -10
  171. package/.next/server/app/index.segments/_full.segment.rsc +0 -21
  172. package/.next/server/app/index.segments/_head.segment.rsc +0 -6
  173. package/.next/server/app/index.segments/_index.segment.rsc +0 -5
  174. package/.next/server/app/index.segments/_tree.segment.rsc +0 -5
  175. package/.next/server/chunks/[externals]__1334_78._.js +0 -3
  176. package/.next/server/chunks/[externals]_next_dist_0arv.vj._.js.map +0 -1
  177. package/.next/server/chunks/[root-of-the-server]__0-lisav._.js +0 -15
  178. package/.next/server/chunks/[root-of-the-server]__0_2_i_q._.js +0 -15
  179. package/.next/server/chunks/[root-of-the-server]__0__9rdk._.js +0 -15
  180. package/.next/server/chunks/[root-of-the-server]__0bi3d5z._.js +0 -15
  181. package/.next/server/chunks/[root-of-the-server]__0iqaxhx._.js +0 -15
  182. package/.next/server/chunks/[root-of-the-server]__0l7c-gn._.js +0 -3
  183. package/.next/server/chunks/[root-of-the-server]__0m91xif._.js +0 -15
  184. package/.next/server/chunks/[root-of-the-server]__0ntt3om._.js +0 -3
  185. package/.next/server/chunks/[root-of-the-server]__0p~u94k._.js +0 -3
  186. package/.next/server/chunks/[root-of-the-server]__0qyrt6_._.js +0 -3
  187. package/.next/server/chunks/[root-of-the-server]__0qz~w0~._.js +0 -15
  188. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js +0 -15
  189. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js.map +0 -1
  190. package/.next/server/chunks/[root-of-the-server]__0ylcu31._.js +0 -3
  191. package/.next/server/chunks/[root-of-the-server]__0ylcu31._.js.map +0 -1
  192. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js +0 -3
  193. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js.map +0 -1
  194. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0m.429v.js +0 -3
  195. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0m.429v.js.map +0 -1
  196. package/.next/server/chunks/ssr/[root-of-the-server]__089dc4i._.js.map +0 -1
  197. package/.next/server/chunks/ssr/[root-of-the-server]__0_7i5h0._.js +0 -3
  198. package/.next/server/chunks/ssr/[root-of-the-server]__0_7i5h0._.js.map +0 -1
  199. package/.next/server/chunks/ssr/[root-of-the-server]__0hw~y-4._.js +0 -3
  200. package/.next/server/chunks/ssr/[root-of-the-server]__0hw~y-4._.js.map +0 -1
  201. package/.next/server/chunks/ssr/[root-of-the-server]__0~y8ue.._.js +0 -3
  202. package/.next/server/chunks/ssr/[root-of-the-server]__0~y8ue.._.js.map +0 -1
  203. package/.next/server/chunks/ssr/node_modules_05l396c._.js +0 -3
  204. package/.next/server/chunks/ssr/node_modules_05l396c._.js.map +0 -1
  205. package/.next/server/chunks/ssr/node_modules_next_dist_0ppctuh._.js.map +0 -1
  206. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03-z2qq.js +0 -4
  207. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03-z2qq.js.map +0 -1
  208. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07vh7rm.js +0 -4
  209. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07vh7rm.js.map +0 -1
  210. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0qp4u6g.js +0 -4
  211. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0qp4u6g.js.map +0 -1
  212. package/.next/server/functions-config-manifest.json +0 -4
  213. package/.next/server/pages/404.html +0 -1
  214. package/.next/server/pages/500.html +0 -1
  215. package/.next/server/prefetch-hints.json +0 -1
  216. package/.next/static/W3UxhE_D02EcHxTzdXKHX/_ssgManifest.js +0 -1
  217. package/.next/static/chunks/01.1lqu6kmllv.js +0 -1
  218. package/.next/static/chunks/0ek0280331wie.js +0 -1
  219. package/.next/static/chunks/0jxar8nsw_py3.js +0 -1
  220. package/.next/static/media/favicon.0x3dzn~oxb6tn.ico +0 -0
  221. package/.next/trace +0 -1
  222. package/.next/trace-build +0 -1
  223. package/src/app/favicon.ico +0 -0
  224. /package/.next/server/chunks/{[externals]__1334_78._.js.map → [externals]__048y_op._.js.map} +0 -0
  225. /package/.next/static/{W3UxhE_D02EcHxTzdXKHX → ZtSBLhTiGqKpNetcymWiC}/_buildManifest.js +0 -0
  226. /package/.next/static/{W3UxhE_D02EcHxTzdXKHX → ZtSBLhTiGqKpNetcymWiC}/_clientMiddlewareManifest.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
+ };
@@ -25,9 +25,9 @@ export default function TerminalConsole({ sessionId, height = "400px", wsPort =
25
25
  // Initialize xterm
26
26
  const term = new Terminal({
27
27
  cursorBlink: true,
28
- fontFamily: "var(--font-mono), monospace",
28
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
29
29
  fontSize: 13,
30
- lineHeight: 1.4,
30
+ lineHeight: 1.2,
31
31
  scrollback: 5000,
32
32
  theme: {
33
33
  background: "#05070a",
@@ -15,6 +15,11 @@ 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
+ },
18
23
  };
19
24
 
20
25
  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;
@@ -115,6 +116,9 @@ export default function Home() {
115
116
  if (data.originalCwd) {
116
117
  setServerCwd(data.originalCwd);
117
118
  }
119
+ if (data.version) {
120
+ setAppVersion(data.version);
121
+ }
118
122
  } catch (err) {
119
123
  console.error("Failed to fetch sessions:", err);
120
124
  }
@@ -140,6 +144,22 @@ export default function Home() {
140
144
  await fetchRecents();
141
145
  };
142
146
  initFetch();
147
+
148
+ // Unregister any active Service Workers to disable PWA interceptions
149
+ if ('serviceWorker' in navigator) {
150
+ navigator.serviceWorker.getRegistrations().then((registrations) => {
151
+ for (const registration of registrations) {
152
+ registration.unregister()
153
+ .then((success) => {
154
+ if (success) {
155
+ console.log('Service Worker unregistered successfully.');
156
+ }
157
+ })
158
+ .catch((err) => console.error('Failed to unregister Service Worker:', err));
159
+ }
160
+ });
161
+ }
162
+
143
163
  const interval = setInterval(fetchSessions, 4000);
144
164
  return () => clearInterval(interval);
145
165
  }, []);
@@ -232,9 +252,12 @@ export default function Home() {
232
252
  <div className={`${styles.container} ${activeSession ? styles.containerActive : ""}`}>
233
253
  {/* Header */}
234
254
  <header className={styles.header}>
235
- <div className={styles.logoArea}>
236
- <h1 className={styles.title}>NeuralLoom</h1>
237
- <span className={styles.subtitle}>Unified AI Orchestrator & Context Bridge</span>
255
+ <div style={{ display: "flex", alignItems: "center", gap: "1.5rem" }}>
256
+ <img src="/logo.png" alt="NeuralLoom Logo" style={{ width: "160px", height: "160px", borderRadius: "16px", objectFit: "contain" }} />
257
+ <div className={styles.logoArea}>
258
+ <h1 className={styles.title} style={{ margin: 0, lineHeight: 1.1 }}>NeuralLoom</h1>
259
+ <span className={styles.subtitle}>Unified AI Orchestrator & Context Bridge</span>
260
+ </div>
238
261
  </div>
239
262
  <div className={styles.statusIndicator}>
240
263
  <span className={styles.pulseDot}></span>
@@ -470,8 +493,22 @@ export default function Home() {
470
493
  </section>
471
494
  )}
472
495
 
473
- {/* Context Hub Staging section */}
496
+ {/* Workspace Shell Terminal */}
474
497
  <section>
498
+ <h2 className={styles.sectionTitle}>Workspace Shell Terminal</h2>
499
+ <div className="card" style={{ display: "flex", flexDirection: "column", border: "1px solid var(--border)", padding: 0 }}>
500
+ <div className={styles.agentHeader} style={{ justifyContent: "space-between", background: "rgba(255, 255, 255, 0.02)" }}>
501
+ <span style={{ fontWeight: 600, display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "0.85rem" }}>
502
+ 💻 Local Workspace Shell (CWD: {serverCwd || "root"})
503
+ </span>
504
+ <span style={{ fontSize: "0.7rem", color: "var(--muted)" }}>Pre-configured environment</span>
505
+ </div>
506
+ <TerminalConsole sessionId="workspace-terminal" height="300px" wsPort={wsPort} />
507
+ </div>
508
+ </section>
509
+
510
+ {/* Context Hub Staging section */}
511
+ <section style={{ marginTop: "2rem" }}>
475
512
  <h2 className={styles.sectionTitle}>Shared Context Hub & Slash Commands</h2>
476
513
  <div className={styles.contextList}>
477
514
  <div className={`${styles.contextItem} card`}>
@@ -538,20 +575,6 @@ export default function Home() {
538
575
  </div>
539
576
  <ContextEditor />
540
577
  </section>
541
-
542
- {/* Workspace Shell Terminal */}
543
- <section style={{ marginTop: "2rem" }}>
544
- <h2 className={styles.sectionTitle}>Workspace Shell Terminal</h2>
545
- <div className="card" style={{ display: "flex", flexDirection: "column", border: "1px solid var(--border)", padding: 0 }}>
546
- <div className={styles.agentHeader} style={{ justifyContent: "space-between", background: "rgba(255, 255, 255, 0.02)" }}>
547
- <span style={{ fontWeight: 600, display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "0.85rem" }}>
548
- 💻 Local Workspace Shell (CWD: {serverCwd || "root"})
549
- </span>
550
- <span style={{ fontSize: "0.7rem", color: "var(--muted)" }}>Pre-configured environment</span>
551
- </div>
552
- <TerminalConsole sessionId="workspace-terminal" height="300px" wsPort={wsPort} />
553
- </div>
554
- </section>
555
578
  </main>
556
579
 
557
580
  {/* Right Panel: Environments & Actions */}
@@ -686,7 +709,7 @@ export default function Home() {
686
709
 
687
710
  {/* Footer */}
688
711
  <footer className={styles.footer}>
689
- <span>NeuralLoom v0.2.13 — Experimental AI Orchestrator Bridge</span>
712
+ <span>NeuralLoom v{appVersion} — Experimental AI Orchestrator Bridge</span>
690
713
  <span>Directory: {serverCwd || "Loading..."}</span>
691
714
  </footer>
692
715