treesap 0.1.10 → 0.1.11

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.
@@ -0,0 +1,7 @@
1
+ interface ChatInputProps {
2
+ id?: string;
3
+ terminalId?: string;
4
+ }
5
+ export declare function ChatInput({ id, terminalId }: ChatInputProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=ChatInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../src/components/ChatInput.tsx"],"names":[],"mappings":"AAAA,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAiB,EAAE,UAAyB,EAAE,EAAE,cAAc,kDAkDzF"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ export function ChatInput({ id = "chat-input", terminalId = "terminal-1" }) {
3
+ return (_jsxs("sapling-island", { loading: "visible", children: [_jsx("template", { children: _jsx("script", { type: "module", src: "/components/ChatInput.js" }) }), _jsx("div", { id: id, class: "border-t border-[#3c3c3c] bg-[#2d2d30] p-3", children: _jsxs("div", { class: "flex items-end gap-2", children: [_jsx("div", { class: "flex-1 relative", children: _jsx("textarea", { id: `${id}-textarea`, placeholder: "Type your command or message...", class: "w-full min-h-[40px] max-h-[120px] px-3 py-2 bg-[#1e1e1e] border border-[#3c3c3c] rounded-lg text-[#cccccc] placeholder-[#888] resize-none focus:outline-none focus:border-[#0e639c] transition-colors text-sm font-mono", rows: 1 }) }), _jsx("button", { type: "button", id: `${id}-send-btn`, class: "px-3 py-2 bg-[#0e639c] hover:bg-[#1177bb] disabled:bg-[#3c3c3c] disabled:text-[#888] text-white rounded-lg transition-colors flex-shrink-0 flex items-center justify-center min-h-[40px]", title: "Send to Input Field", children: _jsx("iconify-icon", { icon: "tabler:arrow-up", width: "16", height: "16" }) }), _jsx("button", { type: "button", id: `${id}-execute-btn`, class: "px-3 py-2 bg-[#28a745] hover:bg-[#218838] disabled:bg-[#3c3c3c] disabled:text-[#888] text-white rounded-lg transition-colors flex-shrink-0 flex items-center justify-center min-h-[40px]", title: "Execute Command", children: _jsx("iconify-icon", { icon: "tabler:player-play", width: "16", height: "16" }) })] }) }), _jsx("script", { dangerouslySetInnerHTML: { __html: `
4
+ // Pass chat input data to JavaScript
5
+ window.chatInputData_${id.replace(/-/g, '_')} = {
6
+ chatInputId: '${id}',
7
+ terminalId: '${terminalId}'
8
+ };
9
+ ` } })] }));
10
+ }
11
+ //# sourceMappingURL=ChatInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInput.js","sourceRoot":"","sources":["../../src/components/ChatInput.tsx"],"names":[],"mappings":";AAKA,MAAM,UAAU,SAAS,CAAC,EAAE,EAAE,GAAG,YAAY,EAAE,UAAU,GAAG,YAAY,EAAkB;IACxF,OAAO,CACL,0BAAgB,OAAO,EAAC,SAAS,aAC/B,6BACE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAC,0BAA0B,GAAU,GACrD,EAEX,cAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAC,4CAA4C,YAC7D,eAAK,KAAK,EAAC,sBAAsB,aAE/B,cAAK,KAAK,EAAC,iBAAiB,YAC1B,mBACE,EAAE,EAAE,GAAG,EAAE,WAAW,EACpB,WAAW,EAAC,iCAAiC,EAC7C,KAAK,EAAC,yNAAyN,EAC/N,IAAI,EAAE,CAAC,GACG,GACR,EAGN,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAE,GAAG,EAAE,WAAW,EACpB,KAAK,EAAC,0LAA0L,EAChM,KAAK,EAAC,qBAAqB,YAE3B,uBAAc,IAAI,EAAC,iBAAiB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACpE,EAGT,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAE,GAAG,EAAE,cAAc,EACvB,KAAK,EAAC,0LAA0L,EAChM,KAAK,EAAC,iBAAiB,YAEvB,uBAAc,IAAI,EAAC,oBAAoB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACvE,IACL,GACF,EAEN,iBAAQ,uBAAuB,EAAE,EAAC,MAAM,EAAE;;+BAEjB,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;0BAC1B,EAAE;yBACH,UAAU;;OAE5B,EAAC,GAAW,IACE,CAClB,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../src/components/Sidebar.tsx"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAc,EAAE,WAAkB,EAAE,gBAAgB,EAAE,EAAE,YAAY,kDAmF7F"}
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../src/components/Sidebar.tsx"],"names":[],"mappings":"AAGA,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAc,EAAE,WAAkB,EAAE,gBAAgB,EAAE,EAAE,YAAY,kDAyF7F"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
2
  import { Terminal as TerminalComponent } from "./Terminal.js";
3
+ import { ChatInput } from "./ChatInput.js";
3
4
  export function Sidebar({ id = "sidebar", previewPort = 1234, workingDirectory }) {
4
- return (_jsxs("sapling-island", { loading: "visible", children: [_jsxs("template", { children: [_jsx("script", { type: "module", src: "https://code.iconify.design/iconify-icon/2.0.0/iconify-icon.min.js" }), _jsx("script", { type: "module", src: "/components/Sidebar.js" })] }), _jsx("div", { id: `${id}-backdrop`, class: "fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm z-40 transition-opacity duration-300 opacity-0 pointer-events-none md:hidden" }), _jsxs("div", { id: `${id}-pane`, class: "fixed left-0 top-0 h-full w-full z-50 transform -translate-x-full transition-transform duration-300 ease-in-out md:relative md:translate-x-0 md:w-2/5 md:z-auto border-r border-[#3c3c3c] flex flex-col bg-[#252526]", children: [_jsx("div", { class: "p-3 border-b border-[#3c3c3c] bg-[#2d2d30] flex-shrink-0", children: _jsxs("div", { class: "flex items-center gap-2", children: [_jsx("button", { type: "button", id: `${id}-close-btn`, class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white md:hidden", title: "Close Sidebar", children: _jsx("iconify-icon", { icon: "tabler:x", width: "16", height: "16" }) }), _jsx("a", { href: "/", class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Back to Home", children: _jsx("iconify-icon", { icon: "tabler:arrow-left", width: "16", height: "16" }) }), _jsx("button", { type: "button", id: "live-preview-refresh-btn", class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Reload", children: _jsx("iconify-icon", { icon: "tabler:refresh", width: "16", height: "16" }) }), _jsxs("div", { class: "flex-1 flex items-center bg-[#1e1e1e] border border-[#3c3c3c] rounded px-3 py-2 hover:border-[#0e639c] focus-within:border-[#0e639c] transition-all", children: [_jsx("iconify-icon", { icon: "tabler:world", width: "16", height: "16", class: "text-[#cccccc] mr-2" }), _jsxs("span", { class: "text-[#cccccc] text-sm", children: ["localhost:", previewPort, "/"] }), _jsx("input", { id: "live-preview-url-input", type: "text", placeholder: "path", defaultValue: "", class: "flex-1 bg-transparent text-sm focus:outline-none text-[#cccccc] ml-1" }), _jsx("button", { type: "button", id: "live-preview-load-btn", class: "ml-2 p-1 hover:bg-[#3c3c3c] rounded transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Go", children: _jsx("iconify-icon", { icon: "tabler:chevron-right", width: "16", height: "16" }) })] })] }) }), _jsx("div", { class: "flex-1 overflow-hidden bg-[#1e1e1e]", children: _jsx("div", { class: "h-full", children: _jsx(TerminalComponent, { index: 1 }) }) })] })] }));
5
+ return (_jsxs("sapling-island", { loading: "visible", children: [_jsxs("template", { children: [_jsx("script", { type: "module", src: "https://code.iconify.design/iconify-icon/2.0.0/iconify-icon.min.js" }), _jsx("script", { type: "module", src: "/components/Sidebar.js" })] }), _jsx("div", { id: `${id}-backdrop`, class: "fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm z-40 transition-opacity duration-300 opacity-0 pointer-events-none md:hidden" }), _jsxs("div", { id: `${id}-pane`, class: "fixed left-0 top-0 h-full w-full z-50 transform -translate-x-full transition-transform duration-300 ease-in-out md:relative md:translate-x-0 md:w-2/5 md:z-auto border-r border-[#3c3c3c] flex flex-col bg-[#252526]", children: [_jsx("div", { class: "p-3 border-b border-[#3c3c3c] bg-[#2d2d30] flex-shrink-0", children: _jsxs("div", { class: "flex items-center gap-2", children: [_jsx("button", { type: "button", id: `${id}-close-btn`, class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white md:hidden", title: "Close Sidebar", children: _jsx("iconify-icon", { icon: "tabler:x", width: "16", height: "16" }) }), _jsx("a", { href: "/", class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Back to Home", children: _jsx("iconify-icon", { icon: "tabler:arrow-left", width: "16", height: "16" }) }), _jsx("button", { type: "button", id: "live-preview-refresh-btn", class: "p-2 hover:bg-[#3c3c3c] rounded-md transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Reload", children: _jsx("iconify-icon", { icon: "tabler:refresh", width: "16", height: "16" }) }), _jsxs("div", { class: "flex-1 flex items-center bg-[#1e1e1e] border border-[#3c3c3c] rounded px-3 py-2 hover:border-[#0e639c] focus-within:border-[#0e639c] transition-all", children: [_jsx("iconify-icon", { icon: "tabler:world", width: "16", height: "16", class: "text-[#cccccc] mr-2" }), _jsxs("span", { class: "text-[#cccccc] text-sm", children: ["localhost:", previewPort, "/"] }), _jsx("input", { id: "live-preview-url-input", type: "text", placeholder: "path", defaultValue: "", class: "flex-1 bg-transparent text-sm focus:outline-none text-[#cccccc] ml-1" }), _jsx("button", { type: "button", id: "live-preview-load-btn", class: "ml-2 p-1 hover:bg-[#3c3c3c] rounded transition-colors flex-shrink-0 text-[#cccccc] hover:text-white", title: "Go", children: _jsx("iconify-icon", { icon: "tabler:chevron-right", width: "16", height: "16" }) })] })] }) }), _jsxs("div", { class: "flex-1 overflow-hidden bg-[#1e1e1e] flex flex-col", children: [_jsx("div", { class: "flex-1 overflow-hidden", children: _jsx(TerminalComponent, { index: 1 }) }), _jsx("div", { class: "flex-shrink-0", children: _jsx(ChatInput, { id: "chat-input", terminalId: "terminal-1" }) })] })] })] }));
5
6
  }
6
7
  //# sourceMappingURL=Sidebar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../src/components/Sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQ9D,MAAM,UAAU,OAAO,CAAC,EAAE,EAAE,GAAG,SAAS,EAAE,WAAW,GAAG,IAAI,EAAE,gBAAgB,EAAgB;IAC5F,OAAO,CACL,0BAAgB,OAAO,EAAC,SAAS,aAC/B,+BACE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAC,oEAAoE,GAAU,EACxG,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAC,wBAAwB,GAAU,IACnD,EAGX,cACE,EAAE,EAAE,GAAG,EAAE,WAAW,EACpB,KAAK,EAAC,oIAAoI,GACrI,EAGP,eACE,EAAE,EAAE,GAAG,EAAE,OAAO,EAChB,KAAK,EAAC,sNAAsN,aAG5N,cAAK,KAAK,EAAC,0DAA0D,YACnE,eAAK,KAAK,EAAC,yBAAyB,aAElC,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAE,GAAG,EAAE,YAAY,EACrB,KAAK,EAAC,6GAA6G,EACnH,KAAK,EAAC,eAAe,YAErB,uBAAc,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GAC7D,EAGT,YACE,IAAI,EAAC,GAAG,EACR,KAAK,EAAC,mGAAmG,EACzG,KAAK,EAAC,cAAc,YAEpB,uBAAc,IAAI,EAAC,mBAAmB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GAC3E,EAGJ,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAC,0BAA0B,EAC7B,KAAK,EAAC,mGAAmG,EACzG,KAAK,EAAC,QAAQ,YAEd,uBAAc,IAAI,EAAC,gBAAgB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACnE,EAGT,eAAK,KAAK,EAAC,qJAAqJ,aAC9J,uBAAc,IAAI,EAAC,cAAc,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC,qBAAqB,GAAgB,EACpG,gBAAM,KAAK,EAAC,wBAAwB,2BAAY,WAAW,SAAS,EACpE,gBACE,EAAE,EAAC,wBAAwB,EAC3B,IAAI,EAAC,MAAM,EACX,WAAW,EAAC,MAAM,EAClB,YAAY,EAAC,EAAE,EACf,KAAK,EAAC,sEAAsE,GAC5E,EACF,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAC,uBAAuB,EAC1B,KAAK,EAAC,qGAAqG,EAC3G,KAAK,EAAC,IAAI,YAEV,uBAAc,IAAI,EAAC,sBAAsB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACzE,IACL,IACF,GACF,EAGN,cAAK,KAAK,EAAC,qCAAqC,YAC9C,cAAK,KAAK,EAAC,QAAQ,YACjB,KAAC,iBAAiB,IAAC,KAAK,EAAE,CAAC,GAAI,GAC3B,GACF,IACF,IACS,CAClB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../src/components/Sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQ3C,MAAM,UAAU,OAAO,CAAC,EAAE,EAAE,GAAG,SAAS,EAAE,WAAW,GAAG,IAAI,EAAE,gBAAgB,EAAgB;IAC5F,OAAO,CACL,0BAAgB,OAAO,EAAC,SAAS,aAC/B,+BACE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAC,oEAAoE,GAAU,EACxG,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAC,wBAAwB,GAAU,IACnD,EAGX,cACE,EAAE,EAAE,GAAG,EAAE,WAAW,EACpB,KAAK,EAAC,oIAAoI,GACrI,EAGP,eACE,EAAE,EAAE,GAAG,EAAE,OAAO,EAChB,KAAK,EAAC,sNAAsN,aAG5N,cAAK,KAAK,EAAC,0DAA0D,YACnE,eAAK,KAAK,EAAC,yBAAyB,aAElC,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAE,GAAG,EAAE,YAAY,EACrB,KAAK,EAAC,6GAA6G,EACnH,KAAK,EAAC,eAAe,YAErB,uBAAc,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GAC7D,EAGT,YACE,IAAI,EAAC,GAAG,EACR,KAAK,EAAC,mGAAmG,EACzG,KAAK,EAAC,cAAc,YAEpB,uBAAc,IAAI,EAAC,mBAAmB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GAC3E,EAGJ,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAC,0BAA0B,EAC7B,KAAK,EAAC,mGAAmG,EACzG,KAAK,EAAC,QAAQ,YAEd,uBAAc,IAAI,EAAC,gBAAgB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACnE,EAGT,eAAK,KAAK,EAAC,qJAAqJ,aAC9J,uBAAc,IAAI,EAAC,cAAc,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC,qBAAqB,GAAgB,EACpG,gBAAM,KAAK,EAAC,wBAAwB,2BAAY,WAAW,SAAS,EACpE,gBACE,EAAE,EAAC,wBAAwB,EAC3B,IAAI,EAAC,MAAM,EACX,WAAW,EAAC,MAAM,EAClB,YAAY,EAAC,EAAE,EACf,KAAK,EAAC,sEAAsE,GAC5E,EACF,iBACE,IAAI,EAAC,QAAQ,EACb,EAAE,EAAC,uBAAuB,EAC1B,KAAK,EAAC,qGAAqG,EAC3G,KAAK,EAAC,IAAI,YAEV,uBAAc,IAAI,EAAC,sBAAsB,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,GAAgB,GACzE,IACL,IACF,GACF,EAGN,eAAK,KAAK,EAAC,mDAAmD,aAE5D,cAAK,KAAK,EAAC,wBAAwB,YACjC,KAAC,iBAAiB,IAAC,KAAK,EAAE,CAAC,GAAI,GAC3B,EAGN,cAAK,KAAK,EAAC,eAAe,YACxB,KAAC,SAAS,IAAC,EAAE,EAAC,YAAY,EAAC,UAAU,EAAC,YAAY,GAAG,GACjD,IACF,IACF,IACS,CAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,237 @@
1
+ // ChatInput component JavaScript for chat-style terminal input
2
+ class ChatInputManager {
3
+ constructor(chatInputId) {
4
+ this.chatInputId = chatInputId;
5
+ this.container = document.getElementById(chatInputId);
6
+ this.textarea = document.getElementById(`${chatInputId}-textarea`);
7
+ this.sendBtn = document.getElementById(`${chatInputId}-send-btn`);
8
+ this.executeBtn = document.getElementById(`${chatInputId}-execute-btn`);
9
+
10
+ // Get chat input data from window
11
+ const chatInputData = window[`chatInputData_${chatInputId.replace(/-/g, '_')}`];
12
+ if (!chatInputData) {
13
+ console.error(`No chat input data found for ${chatInputId}`);
14
+ return;
15
+ }
16
+
17
+ this.terminalId = chatInputData.terminalId;
18
+
19
+ console.log(`ChatInput ${chatInputId} initialized for terminal:`, this.terminalId);
20
+
21
+ this.init();
22
+ }
23
+
24
+ init() {
25
+ if (!this.container || !this.textarea || !this.sendBtn || !this.executeBtn) {
26
+ console.error('ChatInput elements not found!', {
27
+ container: !!this.container,
28
+ textarea: !!this.textarea,
29
+ sendBtn: !!this.sendBtn,
30
+ executeBtn: !!this.executeBtn
31
+ });
32
+ return;
33
+ }
34
+
35
+ this.setupEventListeners();
36
+ this.setupAutoResize();
37
+ }
38
+
39
+ setupEventListeners() {
40
+ // Handle send to input button click
41
+ this.sendBtn.addEventListener('click', () => {
42
+ this.sendToInput();
43
+ });
44
+
45
+ // Handle execute button click
46
+ this.executeBtn.addEventListener('click', () => {
47
+ this.executeCommand();
48
+ });
49
+
50
+ // Handle textarea key events
51
+ this.textarea.addEventListener('keydown', (e) => {
52
+ // Enter without shift sends to input field
53
+ if (e.key === 'Enter' && !e.shiftKey) {
54
+ e.preventDefault();
55
+ this.sendToInput();
56
+ }
57
+ // Ctrl+Enter or Cmd+Enter executes
58
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
59
+ e.preventDefault();
60
+ this.executeCommand();
61
+ }
62
+ // Allow Shift+Enter for new lines (default behavior)
63
+ });
64
+
65
+ // Handle input to auto-resize textarea
66
+ this.textarea.addEventListener('input', () => {
67
+ this.adjustTextareaHeight();
68
+ });
69
+
70
+ // Disable buttons when textarea is empty
71
+ this.textarea.addEventListener('input', () => {
72
+ this.updateButtonState();
73
+ });
74
+
75
+ // Focus textarea when container is clicked
76
+ this.container.addEventListener('click', (e) => {
77
+ if (e.target === this.container) {
78
+ this.textarea.focus();
79
+ }
80
+ });
81
+ }
82
+
83
+ setupAutoResize() {
84
+ // Initial state
85
+ this.adjustTextareaHeight();
86
+ this.updateButtonState();
87
+ }
88
+
89
+ adjustTextareaHeight() {
90
+ if (!this.textarea) return;
91
+
92
+ // Reset height to auto to get the correct scrollHeight
93
+ this.textarea.style.height = 'auto';
94
+
95
+ // Calculate the new height based on content
96
+ const scrollHeight = this.textarea.scrollHeight;
97
+ const maxHeight = 120; // Max height from CSS
98
+ const minHeight = 40; // Min height from CSS
99
+
100
+ const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
101
+ this.textarea.style.height = `${newHeight}px`;
102
+ }
103
+
104
+ updateButtonState() {
105
+ if (!this.textarea || !this.sendBtn || !this.executeBtn) return;
106
+
107
+ const hasContent = this.textarea.value.trim().length > 0;
108
+ this.sendBtn.disabled = !hasContent;
109
+ this.executeBtn.disabled = false; // Execute button is always enabled
110
+ }
111
+
112
+ sendToInput() {
113
+ if (!this.textarea) return;
114
+
115
+ const input = this.textarea.value.trim();
116
+ if (!input) return;
117
+
118
+ // Get the terminal manager for the associated terminal
119
+ const terminalManager = this.getTerminalManager();
120
+ if (!terminalManager) {
121
+ console.error(`No terminal manager found for terminal: ${this.terminalId}`);
122
+ return;
123
+ }
124
+
125
+ // Send the input to the terminal input field (without executing)
126
+ console.log(`Sending text to terminal input field ${this.terminalId}:`, input);
127
+ console.log('ChatInput sending (programmatic):', JSON.stringify(input), 'char codes:', input.split('').map(c => c.charCodeAt(0)));
128
+
129
+ // Simplest approach: just send the text directly
130
+ // This should work for most cases in Claude Code
131
+ terminalManager.sendInput(input);
132
+
133
+ // Clear the textarea
134
+ this.textarea.value = '';
135
+ this.adjustTextareaHeight();
136
+ this.updateButtonState();
137
+
138
+ // Focus back to textarea for next input
139
+ this.textarea.focus();
140
+ }
141
+
142
+ executeCommand() {
143
+ // Get the terminal manager for the associated terminal
144
+ const terminalManager = this.getTerminalManager();
145
+ if (!terminalManager) {
146
+ console.error(`No terminal manager found for terminal: ${this.terminalId}`);
147
+ return;
148
+ }
149
+
150
+ // Execute whatever is currently in the terminal input field
151
+ console.log(`Executing command in terminal ${this.terminalId}`);
152
+ console.log('ChatInput executing (programmatic):', JSON.stringify('\r'), 'char code:', '\r'.charCodeAt(0));
153
+ terminalManager.sendInput('\r');
154
+ }
155
+
156
+ getTerminalManager() {
157
+ // Access global terminal managers
158
+ if (window.terminalManagers && window.terminalManagers.has(this.terminalId)) {
159
+ return window.terminalManagers.get(this.terminalId);
160
+ }
161
+
162
+ // Fallback: try to find by checking all managers
163
+ if (window.terminalManagers) {
164
+ for (const [terminalId, manager] of window.terminalManagers.entries()) {
165
+ if (terminalId === this.terminalId) {
166
+ return manager;
167
+ }
168
+ }
169
+ }
170
+
171
+ // If not found, log available terminals for debugging
172
+ if (window.terminalManagers) {
173
+ console.log('Available terminal managers:', Array.from(window.terminalManagers.keys()));
174
+ console.log('Looking for terminal ID:', this.terminalId);
175
+ } else {
176
+ console.log('No terminal managers found in window object');
177
+ }
178
+
179
+ return null;
180
+ }
181
+
182
+ focus() {
183
+ if (this.textarea) {
184
+ this.textarea.focus();
185
+ }
186
+ }
187
+
188
+ destroy() {
189
+ // Clean up event listeners if needed
190
+ // The component will be removed from DOM, so most cleanup is automatic
191
+ }
192
+ }
193
+
194
+ // Auto-initialize when script loads
195
+ console.log('ChatInput.js loaded, looking for chat input containers...');
196
+ let chatInputManagers = new Map();
197
+
198
+ function initializeChatInputs() {
199
+ // Look for all sapling-islands with chat input content
200
+ const saplingIslands = document.querySelectorAll('sapling-island');
201
+
202
+ for (const island of saplingIslands) {
203
+ // Look for chat input container
204
+ const chatInputDiv = island.querySelector('div[id*="chat-input"]');
205
+ if (chatInputDiv && chatInputDiv.id) {
206
+ const chatInputId = chatInputDiv.id;
207
+ console.log('Found chat input component with ID:', chatInputId);
208
+
209
+ // Check if we already have a manager for this chat input
210
+ if (!chatInputManagers.has(chatInputId)) {
211
+ const manager = new ChatInputManager(chatInputId);
212
+ chatInputManagers.set(chatInputId, manager);
213
+ }
214
+ }
215
+ }
216
+
217
+ console.log(`Initialized ${chatInputManagers.size} chat input(s)`);
218
+ }
219
+
220
+ // Try immediate initialization
221
+ if (document.readyState === 'loading') {
222
+ document.addEventListener('DOMContentLoaded', initializeChatInputs);
223
+ } else {
224
+ initializeChatInputs();
225
+ }
226
+
227
+ // Make available globally
228
+ window.chatInputManagers = chatInputManagers;
229
+ window.ChatInputManager = ChatInputManager;
230
+ window.initializeChatInputs = initializeChatInputs;
231
+
232
+ // Cleanup on page unload
233
+ window.addEventListener('beforeunload', () => {
234
+ for (const manager of chatInputManagers.values()) {
235
+ manager.destroy();
236
+ }
237
+ });
@@ -99,6 +99,8 @@ class TerminalManager {
99
99
 
100
100
  // Handle terminal input - pass through to shell
101
101
  this.terminal.onData((data) => {
102
+ // Log the input data for debugging
103
+ console.log('Terminal input (manual typing):', JSON.stringify(data), 'char codes:', data.split('').map(c => c.charCodeAt(0)));
102
104
  // Send all input directly to the shell session
103
105
  this.sendInput(data);
104
106
  });
@@ -1221,6 +1221,14 @@ video {
1221
1221
  height: 100vh;
1222
1222
  }
1223
1223
 
1224
+ .max-h-\[120px\] {
1225
+ max-height: 120px;
1226
+ }
1227
+
1228
+ .min-h-\[40px\] {
1229
+ min-height: 40px;
1230
+ }
1231
+
1224
1232
  .min-h-screen {
1225
1233
  min-height: 100vh;
1226
1234
  }
@@ -1281,6 +1289,10 @@ video {
1281
1289
  cursor: not-allowed;
1282
1290
  }
1283
1291
 
1292
+ .resize-none {
1293
+ resize: none;
1294
+ }
1295
+
1284
1296
  .resize {
1285
1297
  resize: both;
1286
1298
  }
@@ -1289,6 +1301,10 @@ video {
1289
1301
  flex-direction: column;
1290
1302
  }
1291
1303
 
1304
+ .items-end {
1305
+ align-items: flex-end;
1306
+ }
1307
+
1292
1308
  .items-center {
1293
1309
  align-items: center;
1294
1310
  }
@@ -1346,6 +1362,10 @@ video {
1346
1362
  border-right-width: 1px;
1347
1363
  }
1348
1364
 
1365
+ .border-t {
1366
+ border-top-width: 1px;
1367
+ }
1368
+
1349
1369
  .border-t-2 {
1350
1370
  border-top-width: 2px;
1351
1371
  }
@@ -1370,6 +1390,11 @@ video {
1370
1390
  border-color: rgb(156 163 175 / var(--tw-border-opacity, 1));
1371
1391
  }
1372
1392
 
1393
+ .bg-\[\#0e639c\] {
1394
+ --tw-bg-opacity: 1;
1395
+ background-color: rgb(14 99 156 / var(--tw-bg-opacity, 1));
1396
+ }
1397
+
1373
1398
  .bg-\[\#1e1e1e\] {
1374
1399
  --tw-bg-opacity: 1;
1375
1400
  background-color: rgb(30 30 30 / var(--tw-bg-opacity, 1));
@@ -1380,6 +1405,11 @@ video {
1380
1405
  background-color: rgb(37 37 38 / var(--tw-bg-opacity, 1));
1381
1406
  }
1382
1407
 
1408
+ .bg-\[\#28a745\] {
1409
+ --tw-bg-opacity: 1;
1410
+ background-color: rgb(40 167 69 / var(--tw-bg-opacity, 1));
1411
+ }
1412
+
1383
1413
  .bg-\[\#2a2a2a\] {
1384
1414
  --tw-bg-opacity: 1;
1385
1415
  background-color: rgb(42 42 42 / var(--tw-bg-opacity, 1));
@@ -1470,6 +1500,10 @@ video {
1470
1500
  text-align: center;
1471
1501
  }
1472
1502
 
1503
+ .font-mono {
1504
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1505
+ }
1506
+
1473
1507
  .font-sans {
1474
1508
  font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1475
1509
  }
@@ -1545,6 +1579,16 @@ video {
1545
1579
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
1546
1580
  }
1547
1581
 
1582
+ .placeholder-\[\#888\]::-moz-placeholder {
1583
+ --tw-placeholder-opacity: 1;
1584
+ color: rgb(136 136 136 / var(--tw-placeholder-opacity, 1));
1585
+ }
1586
+
1587
+ .placeholder-\[\#888\]::placeholder {
1588
+ --tw-placeholder-opacity: 1;
1589
+ color: rgb(136 136 136 / var(--tw-placeholder-opacity, 1));
1590
+ }
1591
+
1548
1592
  .opacity-0 {
1549
1593
  opacity: 0;
1550
1594
  }
@@ -1615,6 +1659,16 @@ video {
1615
1659
  border-color: rgb(14 99 156 / var(--tw-border-opacity, 1));
1616
1660
  }
1617
1661
 
1662
+ .hover\:bg-\[\#1177bb\]:hover {
1663
+ --tw-bg-opacity: 1;
1664
+ background-color: rgb(17 119 187 / var(--tw-bg-opacity, 1));
1665
+ }
1666
+
1667
+ .hover\:bg-\[\#218838\]:hover {
1668
+ --tw-bg-opacity: 1;
1669
+ background-color: rgb(33 136 56 / var(--tw-bg-opacity, 1));
1670
+ }
1671
+
1618
1672
  .hover\:bg-\[\#2d2d30\]:hover {
1619
1673
  --tw-bg-opacity: 1;
1620
1674
  background-color: rgb(45 45 48 / var(--tw-bg-opacity, 1));
@@ -1651,11 +1705,26 @@ video {
1651
1705
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1652
1706
  }
1653
1707
 
1708
+ .focus\:border-\[\#0e639c\]:focus {
1709
+ --tw-border-opacity: 1;
1710
+ border-color: rgb(14 99 156 / var(--tw-border-opacity, 1));
1711
+ }
1712
+
1654
1713
  .focus\:outline-none:focus {
1655
1714
  outline: 2px solid transparent;
1656
1715
  outline-offset: 2px;
1657
1716
  }
1658
1717
 
1718
+ .disabled\:bg-\[\#3c3c3c\]:disabled {
1719
+ --tw-bg-opacity: 1;
1720
+ background-color: rgb(60 60 60 / var(--tw-bg-opacity, 1));
1721
+ }
1722
+
1723
+ .disabled\:text-\[\#888\]:disabled {
1724
+ --tw-text-opacity: 1;
1725
+ color: rgb(136 136 136 / var(--tw-text-opacity, 1));
1726
+ }
1727
+
1659
1728
  .group:hover .group-hover\:text-white {
1660
1729
  --tw-text-opacity: 1;
1661
1730
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "treesap",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "AI Agent Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,56 @@
1
+ interface ChatInputProps {
2
+ id?: string;
3
+ terminalId?: string;
4
+ }
5
+
6
+ export function ChatInput({ id = "chat-input", terminalId = "terminal-1" }: ChatInputProps) {
7
+ return (
8
+ <sapling-island loading="visible">
9
+ <template>
10
+ <script type="module" src="/components/ChatInput.js"></script>
11
+ </template>
12
+
13
+ <div id={id} class="border-t border-[#3c3c3c] bg-[#2d2d30] p-3">
14
+ <div class="flex items-end gap-2">
15
+ {/* Textarea for multi-line input */}
16
+ <div class="flex-1 relative">
17
+ <textarea
18
+ id={`${id}-textarea`}
19
+ placeholder="Type your command or message..."
20
+ class="w-full min-h-[40px] max-h-[120px] px-3 py-2 bg-[#1e1e1e] border border-[#3c3c3c] rounded-lg text-[#cccccc] placeholder-[#888] resize-none focus:outline-none focus:border-[#0e639c] transition-colors text-sm font-mono"
21
+ rows={1}
22
+ ></textarea>
23
+ </div>
24
+
25
+ {/* Send to Input button */}
26
+ <button
27
+ type="button"
28
+ id={`${id}-send-btn`}
29
+ class="px-3 py-2 bg-[#0e639c] hover:bg-[#1177bb] disabled:bg-[#3c3c3c] disabled:text-[#888] text-white rounded-lg transition-colors flex-shrink-0 flex items-center justify-center min-h-[40px]"
30
+ title="Send to Input Field"
31
+ >
32
+ <iconify-icon icon="tabler:arrow-up" width="16" height="16"></iconify-icon>
33
+ </button>
34
+
35
+ {/* Execute button */}
36
+ <button
37
+ type="button"
38
+ id={`${id}-execute-btn`}
39
+ class="px-3 py-2 bg-[#28a745] hover:bg-[#218838] disabled:bg-[#3c3c3c] disabled:text-[#888] text-white rounded-lg transition-colors flex-shrink-0 flex items-center justify-center min-h-[40px]"
40
+ title="Execute Command"
41
+ >
42
+ <iconify-icon icon="tabler:player-play" width="16" height="16"></iconify-icon>
43
+ </button>
44
+ </div>
45
+ </div>
46
+
47
+ <script dangerouslySetInnerHTML={{__html: `
48
+ // Pass chat input data to JavaScript
49
+ window.chatInputData_${id.replace(/-/g, '_')} = {
50
+ chatInputId: '${id}',
51
+ terminalId: '${terminalId}'
52
+ };
53
+ `}}></script>
54
+ </sapling-island>
55
+ );
56
+ }
@@ -1,4 +1,5 @@
1
1
  import { Terminal as TerminalComponent } from "./Terminal.js";
2
+ import { ChatInput } from "./ChatInput.js";
2
3
 
3
4
  interface SidebarProps {
4
5
  id?: string;
@@ -81,10 +82,16 @@ export function Sidebar({ id = "sidebar", previewPort = 1234, workingDirectory }
81
82
  </div>
82
83
 
83
84
  {/* Terminal Content */}
84
- <div class="flex-1 overflow-hidden bg-[#1e1e1e]">
85
- <div class="h-full">
85
+ <div class="flex-1 overflow-hidden bg-[#1e1e1e] flex flex-col">
86
+ {/* Terminal Display */}
87
+ <div class="flex-1 overflow-hidden">
86
88
  <TerminalComponent index={1} />
87
89
  </div>
90
+
91
+ {/* Chat Input */}
92
+ <div class="flex-shrink-0">
93
+ <ChatInput id="chat-input" terminalId="terminal-1" />
94
+ </div>
88
95
  </div>
89
96
  </div>
90
97
  </sapling-island>
@@ -0,0 +1,237 @@
1
+ // ChatInput component JavaScript for chat-style terminal input
2
+ class ChatInputManager {
3
+ constructor(chatInputId) {
4
+ this.chatInputId = chatInputId;
5
+ this.container = document.getElementById(chatInputId);
6
+ this.textarea = document.getElementById(`${chatInputId}-textarea`);
7
+ this.sendBtn = document.getElementById(`${chatInputId}-send-btn`);
8
+ this.executeBtn = document.getElementById(`${chatInputId}-execute-btn`);
9
+
10
+ // Get chat input data from window
11
+ const chatInputData = window[`chatInputData_${chatInputId.replace(/-/g, '_')}`];
12
+ if (!chatInputData) {
13
+ console.error(`No chat input data found for ${chatInputId}`);
14
+ return;
15
+ }
16
+
17
+ this.terminalId = chatInputData.terminalId;
18
+
19
+ console.log(`ChatInput ${chatInputId} initialized for terminal:`, this.terminalId);
20
+
21
+ this.init();
22
+ }
23
+
24
+ init() {
25
+ if (!this.container || !this.textarea || !this.sendBtn || !this.executeBtn) {
26
+ console.error('ChatInput elements not found!', {
27
+ container: !!this.container,
28
+ textarea: !!this.textarea,
29
+ sendBtn: !!this.sendBtn,
30
+ executeBtn: !!this.executeBtn
31
+ });
32
+ return;
33
+ }
34
+
35
+ this.setupEventListeners();
36
+ this.setupAutoResize();
37
+ }
38
+
39
+ setupEventListeners() {
40
+ // Handle send to input button click
41
+ this.sendBtn.addEventListener('click', () => {
42
+ this.sendToInput();
43
+ });
44
+
45
+ // Handle execute button click
46
+ this.executeBtn.addEventListener('click', () => {
47
+ this.executeCommand();
48
+ });
49
+
50
+ // Handle textarea key events
51
+ this.textarea.addEventListener('keydown', (e) => {
52
+ // Enter without shift sends to input field
53
+ if (e.key === 'Enter' && !e.shiftKey) {
54
+ e.preventDefault();
55
+ this.sendToInput();
56
+ }
57
+ // Ctrl+Enter or Cmd+Enter executes
58
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
59
+ e.preventDefault();
60
+ this.executeCommand();
61
+ }
62
+ // Allow Shift+Enter for new lines (default behavior)
63
+ });
64
+
65
+ // Handle input to auto-resize textarea
66
+ this.textarea.addEventListener('input', () => {
67
+ this.adjustTextareaHeight();
68
+ });
69
+
70
+ // Disable buttons when textarea is empty
71
+ this.textarea.addEventListener('input', () => {
72
+ this.updateButtonState();
73
+ });
74
+
75
+ // Focus textarea when container is clicked
76
+ this.container.addEventListener('click', (e) => {
77
+ if (e.target === this.container) {
78
+ this.textarea.focus();
79
+ }
80
+ });
81
+ }
82
+
83
+ setupAutoResize() {
84
+ // Initial state
85
+ this.adjustTextareaHeight();
86
+ this.updateButtonState();
87
+ }
88
+
89
+ adjustTextareaHeight() {
90
+ if (!this.textarea) return;
91
+
92
+ // Reset height to auto to get the correct scrollHeight
93
+ this.textarea.style.height = 'auto';
94
+
95
+ // Calculate the new height based on content
96
+ const scrollHeight = this.textarea.scrollHeight;
97
+ const maxHeight = 120; // Max height from CSS
98
+ const minHeight = 40; // Min height from CSS
99
+
100
+ const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
101
+ this.textarea.style.height = `${newHeight}px`;
102
+ }
103
+
104
+ updateButtonState() {
105
+ if (!this.textarea || !this.sendBtn || !this.executeBtn) return;
106
+
107
+ const hasContent = this.textarea.value.trim().length > 0;
108
+ this.sendBtn.disabled = !hasContent;
109
+ this.executeBtn.disabled = false; // Execute button is always enabled
110
+ }
111
+
112
+ sendToInput() {
113
+ if (!this.textarea) return;
114
+
115
+ const input = this.textarea.value.trim();
116
+ if (!input) return;
117
+
118
+ // Get the terminal manager for the associated terminal
119
+ const terminalManager = this.getTerminalManager();
120
+ if (!terminalManager) {
121
+ console.error(`No terminal manager found for terminal: ${this.terminalId}`);
122
+ return;
123
+ }
124
+
125
+ // Send the input to the terminal input field (without executing)
126
+ console.log(`Sending text to terminal input field ${this.terminalId}:`, input);
127
+ console.log('ChatInput sending (programmatic):', JSON.stringify(input), 'char codes:', input.split('').map(c => c.charCodeAt(0)));
128
+
129
+ // Simplest approach: just send the text directly
130
+ // This should work for most cases in Claude Code
131
+ terminalManager.sendInput(input);
132
+
133
+ // Clear the textarea
134
+ this.textarea.value = '';
135
+ this.adjustTextareaHeight();
136
+ this.updateButtonState();
137
+
138
+ // Focus back to textarea for next input
139
+ this.textarea.focus();
140
+ }
141
+
142
+ executeCommand() {
143
+ // Get the terminal manager for the associated terminal
144
+ const terminalManager = this.getTerminalManager();
145
+ if (!terminalManager) {
146
+ console.error(`No terminal manager found for terminal: ${this.terminalId}`);
147
+ return;
148
+ }
149
+
150
+ // Execute whatever is currently in the terminal input field
151
+ console.log(`Executing command in terminal ${this.terminalId}`);
152
+ console.log('ChatInput executing (programmatic):', JSON.stringify('\r'), 'char code:', '\r'.charCodeAt(0));
153
+ terminalManager.sendInput('\r');
154
+ }
155
+
156
+ getTerminalManager() {
157
+ // Access global terminal managers
158
+ if (window.terminalManagers && window.terminalManagers.has(this.terminalId)) {
159
+ return window.terminalManagers.get(this.terminalId);
160
+ }
161
+
162
+ // Fallback: try to find by checking all managers
163
+ if (window.terminalManagers) {
164
+ for (const [terminalId, manager] of window.terminalManagers.entries()) {
165
+ if (terminalId === this.terminalId) {
166
+ return manager;
167
+ }
168
+ }
169
+ }
170
+
171
+ // If not found, log available terminals for debugging
172
+ if (window.terminalManagers) {
173
+ console.log('Available terminal managers:', Array.from(window.terminalManagers.keys()));
174
+ console.log('Looking for terminal ID:', this.terminalId);
175
+ } else {
176
+ console.log('No terminal managers found in window object');
177
+ }
178
+
179
+ return null;
180
+ }
181
+
182
+ focus() {
183
+ if (this.textarea) {
184
+ this.textarea.focus();
185
+ }
186
+ }
187
+
188
+ destroy() {
189
+ // Clean up event listeners if needed
190
+ // The component will be removed from DOM, so most cleanup is automatic
191
+ }
192
+ }
193
+
194
+ // Auto-initialize when script loads
195
+ console.log('ChatInput.js loaded, looking for chat input containers...');
196
+ let chatInputManagers = new Map();
197
+
198
+ function initializeChatInputs() {
199
+ // Look for all sapling-islands with chat input content
200
+ const saplingIslands = document.querySelectorAll('sapling-island');
201
+
202
+ for (const island of saplingIslands) {
203
+ // Look for chat input container
204
+ const chatInputDiv = island.querySelector('div[id*="chat-input"]');
205
+ if (chatInputDiv && chatInputDiv.id) {
206
+ const chatInputId = chatInputDiv.id;
207
+ console.log('Found chat input component with ID:', chatInputId);
208
+
209
+ // Check if we already have a manager for this chat input
210
+ if (!chatInputManagers.has(chatInputId)) {
211
+ const manager = new ChatInputManager(chatInputId);
212
+ chatInputManagers.set(chatInputId, manager);
213
+ }
214
+ }
215
+ }
216
+
217
+ console.log(`Initialized ${chatInputManagers.size} chat input(s)`);
218
+ }
219
+
220
+ // Try immediate initialization
221
+ if (document.readyState === 'loading') {
222
+ document.addEventListener('DOMContentLoaded', initializeChatInputs);
223
+ } else {
224
+ initializeChatInputs();
225
+ }
226
+
227
+ // Make available globally
228
+ window.chatInputManagers = chatInputManagers;
229
+ window.ChatInputManager = ChatInputManager;
230
+ window.initializeChatInputs = initializeChatInputs;
231
+
232
+ // Cleanup on page unload
233
+ window.addEventListener('beforeunload', () => {
234
+ for (const manager of chatInputManagers.values()) {
235
+ manager.destroy();
236
+ }
237
+ });
@@ -99,6 +99,8 @@ class TerminalManager {
99
99
 
100
100
  // Handle terminal input - pass through to shell
101
101
  this.terminal.onData((data) => {
102
+ // Log the input data for debugging
103
+ console.log('Terminal input (manual typing):', JSON.stringify(data), 'char codes:', data.split('').map(c => c.charCodeAt(0)));
102
104
  // Send all input directly to the shell session
103
105
  this.sendInput(data);
104
106
  });
@@ -1221,6 +1221,14 @@ video {
1221
1221
  height: 100vh;
1222
1222
  }
1223
1223
 
1224
+ .max-h-\[120px\] {
1225
+ max-height: 120px;
1226
+ }
1227
+
1228
+ .min-h-\[40px\] {
1229
+ min-height: 40px;
1230
+ }
1231
+
1224
1232
  .min-h-screen {
1225
1233
  min-height: 100vh;
1226
1234
  }
@@ -1281,6 +1289,10 @@ video {
1281
1289
  cursor: not-allowed;
1282
1290
  }
1283
1291
 
1292
+ .resize-none {
1293
+ resize: none;
1294
+ }
1295
+
1284
1296
  .resize {
1285
1297
  resize: both;
1286
1298
  }
@@ -1289,6 +1301,10 @@ video {
1289
1301
  flex-direction: column;
1290
1302
  }
1291
1303
 
1304
+ .items-end {
1305
+ align-items: flex-end;
1306
+ }
1307
+
1292
1308
  .items-center {
1293
1309
  align-items: center;
1294
1310
  }
@@ -1346,6 +1362,10 @@ video {
1346
1362
  border-right-width: 1px;
1347
1363
  }
1348
1364
 
1365
+ .border-t {
1366
+ border-top-width: 1px;
1367
+ }
1368
+
1349
1369
  .border-t-2 {
1350
1370
  border-top-width: 2px;
1351
1371
  }
@@ -1370,6 +1390,11 @@ video {
1370
1390
  border-color: rgb(156 163 175 / var(--tw-border-opacity, 1));
1371
1391
  }
1372
1392
 
1393
+ .bg-\[\#0e639c\] {
1394
+ --tw-bg-opacity: 1;
1395
+ background-color: rgb(14 99 156 / var(--tw-bg-opacity, 1));
1396
+ }
1397
+
1373
1398
  .bg-\[\#1e1e1e\] {
1374
1399
  --tw-bg-opacity: 1;
1375
1400
  background-color: rgb(30 30 30 / var(--tw-bg-opacity, 1));
@@ -1380,6 +1405,11 @@ video {
1380
1405
  background-color: rgb(37 37 38 / var(--tw-bg-opacity, 1));
1381
1406
  }
1382
1407
 
1408
+ .bg-\[\#28a745\] {
1409
+ --tw-bg-opacity: 1;
1410
+ background-color: rgb(40 167 69 / var(--tw-bg-opacity, 1));
1411
+ }
1412
+
1383
1413
  .bg-\[\#2a2a2a\] {
1384
1414
  --tw-bg-opacity: 1;
1385
1415
  background-color: rgb(42 42 42 / var(--tw-bg-opacity, 1));
@@ -1470,6 +1500,10 @@ video {
1470
1500
  text-align: center;
1471
1501
  }
1472
1502
 
1503
+ .font-mono {
1504
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1505
+ }
1506
+
1473
1507
  .font-sans {
1474
1508
  font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1475
1509
  }
@@ -1545,6 +1579,16 @@ video {
1545
1579
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
1546
1580
  }
1547
1581
 
1582
+ .placeholder-\[\#888\]::-moz-placeholder {
1583
+ --tw-placeholder-opacity: 1;
1584
+ color: rgb(136 136 136 / var(--tw-placeholder-opacity, 1));
1585
+ }
1586
+
1587
+ .placeholder-\[\#888\]::placeholder {
1588
+ --tw-placeholder-opacity: 1;
1589
+ color: rgb(136 136 136 / var(--tw-placeholder-opacity, 1));
1590
+ }
1591
+
1548
1592
  .opacity-0 {
1549
1593
  opacity: 0;
1550
1594
  }
@@ -1615,6 +1659,16 @@ video {
1615
1659
  border-color: rgb(14 99 156 / var(--tw-border-opacity, 1));
1616
1660
  }
1617
1661
 
1662
+ .hover\:bg-\[\#1177bb\]:hover {
1663
+ --tw-bg-opacity: 1;
1664
+ background-color: rgb(17 119 187 / var(--tw-bg-opacity, 1));
1665
+ }
1666
+
1667
+ .hover\:bg-\[\#218838\]:hover {
1668
+ --tw-bg-opacity: 1;
1669
+ background-color: rgb(33 136 56 / var(--tw-bg-opacity, 1));
1670
+ }
1671
+
1618
1672
  .hover\:bg-\[\#2d2d30\]:hover {
1619
1673
  --tw-bg-opacity: 1;
1620
1674
  background-color: rgb(45 45 48 / var(--tw-bg-opacity, 1));
@@ -1651,11 +1705,26 @@ video {
1651
1705
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1652
1706
  }
1653
1707
 
1708
+ .focus\:border-\[\#0e639c\]:focus {
1709
+ --tw-border-opacity: 1;
1710
+ border-color: rgb(14 99 156 / var(--tw-border-opacity, 1));
1711
+ }
1712
+
1654
1713
  .focus\:outline-none:focus {
1655
1714
  outline: 2px solid transparent;
1656
1715
  outline-offset: 2px;
1657
1716
  }
1658
1717
 
1718
+ .disabled\:bg-\[\#3c3c3c\]:disabled {
1719
+ --tw-bg-opacity: 1;
1720
+ background-color: rgb(60 60 60 / var(--tw-bg-opacity, 1));
1721
+ }
1722
+
1723
+ .disabled\:text-\[\#888\]:disabled {
1724
+ --tw-text-opacity: 1;
1725
+ color: rgb(136 136 136 / var(--tw-text-opacity, 1));
1726
+ }
1727
+
1659
1728
  .group:hover .group-hover\:text-white {
1660
1729
  --tw-text-opacity: 1;
1661
1730
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));