pi-tau-web-server 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +303 -0
- package/bin/auth.js +96 -0
- package/bin/config.js +99 -0
- package/bin/model-utils.js +134 -0
- package/bin/server-main.js +1189 -0
- package/bin/sessions.js +492 -0
- package/bin/tau.js +8 -0
- package/bin/tree.js +248 -0
- package/bin/types.js +2 -0
- package/package.json +74 -0
- package/public/app-main.js +2025 -0
- package/public/app-types.js +1 -0
- package/public/app.js +1 -0
- package/public/command-palette.js +42 -0
- package/public/dialogs.js +199 -0
- package/public/file-browser.js +196 -0
- package/public/icons/apple-touch-icon.png +0 -0
- package/public/icons/favicon-16.png +0 -0
- package/public/icons/favicon-32.png +0 -0
- package/public/icons/tau-192.png +0 -0
- package/public/icons/tau-512.png +0 -0
- package/public/icons/tau-logo.png +0 -0
- package/public/icons/tau-logo.svg +13 -0
- package/public/icons/tau-maskable-512.png +0 -0
- package/public/icons/tau-new.png +0 -0
- package/public/index.html +264 -0
- package/public/launcher-panel.js +54 -0
- package/public/launcher.js +84 -0
- package/public/manifest.json +28 -0
- package/public/markdown.js +336 -0
- package/public/message-renderer.js +268 -0
- package/public/model-picker.js +478 -0
- package/public/session-sidebar.js +460 -0
- package/public/session-stats-card.js +123 -0
- package/public/state.js +81 -0
- package/public/style.css +3864 -0
- package/public/sw.js +66 -0
- package/public/themes.js +72 -0
- package/public/tool-card.js +317 -0
- package/public/tree-view.js +474 -0
- package/public/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/public/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/public/vendor/katex/katex.min.css +1 -0
- package/public/vendor/katex/katex.min.js +1 -0
- package/public/voice-input.js +74 -0
- package/public/websocket-client.js +156 -0
- package/scripts/copy-katex.mjs +32 -0
- package/src/pi-extension/tau-tree.ts +71 -0
- package/src/public/app-main.ts +2190 -0
- package/src/public/app-types.ts +96 -0
- package/src/public/app.ts +1 -0
- package/src/public/command-palette.ts +53 -0
- package/src/public/dialogs.ts +251 -0
- package/src/public/file-browser.ts +224 -0
- package/src/public/launcher-panel.ts +68 -0
- package/src/public/launcher.ts +101 -0
- package/src/public/legacy-dom.d.ts +29 -0
- package/src/public/markdown.ts +372 -0
- package/src/public/message-renderer.ts +311 -0
- package/src/public/model-picker.ts +500 -0
- package/src/public/session-sidebar.ts +522 -0
- package/src/public/session-stats-card.ts +176 -0
- package/src/public/state.ts +96 -0
- package/src/public/sw.ts +79 -0
- package/src/public/themes.ts +73 -0
- package/src/public/tool-card.ts +375 -0
- package/src/public/tree-view.ts +527 -0
- package/src/public/voice-input.ts +98 -0
- package/src/public/websocket-client.ts +165 -0
- package/src/server/auth.ts +88 -0
- package/src/server/config.ts +88 -0
- package/src/server/model-utils.ts +122 -0
- package/src/server/server-main.ts +1004 -0
- package/src/server/sessions.ts +481 -0
- package/src/server/tau.ts +9 -0
- package/src/server/tree.ts +288 -0
- package/src/server/types.ts +68 -0
- package/tsconfig.json +3 -0
- package/tsconfig.public.json +15 -0
- package/tsconfig.server.json +16 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Tau</title>
|
|
7
|
+
<link rel="icon" type="image/png" href="icons/tau-192.png">
|
|
8
|
+
<link rel="manifest" href="manifest.json">
|
|
9
|
+
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
|
|
10
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
11
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
12
|
+
<meta name="apple-mobile-web-app-title" content="Tau">
|
|
13
|
+
<meta name="theme-color" content="#f5f3ef">
|
|
14
|
+
<link rel="stylesheet" href="style.css">
|
|
15
|
+
<link rel="stylesheet" href="vendor/katex/katex.min.css">
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<div class="app-layout">
|
|
19
|
+
<!-- Left Sidebar -->
|
|
20
|
+
<div class="sidebar" id="sidebar">
|
|
21
|
+
<div class="sidebar-header">
|
|
22
|
+
<div class="mode-toggle" id="mode-toggle">
|
|
23
|
+
<span class="mode-link active" title="Chat"><img src="icons/tau-192.png" alt="τ" class="tau-icon"></span>
|
|
24
|
+
</div>
|
|
25
|
+
<input type="text" id="session-search-input" class="sidebar-search-input" placeholder="Search..." autocomplete="off" />
|
|
26
|
+
<div class="sidebar-actions">
|
|
27
|
+
<button class="icon-btn" id="new-session-btn" title="New session" aria-label="New session"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
|
|
28
|
+
<button class="icon-btn" id="refresh-sessions-btn" title="Refresh sessions" aria-label="Refresh sessions"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg></button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="session-list" id="session-list">
|
|
32
|
+
<div class="session-loading">Loading sessions...</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- Sidebar overlay for mobile -->
|
|
37
|
+
<div class="sidebar-overlay" id="sidebar-overlay"></div>
|
|
38
|
+
|
|
39
|
+
<!-- Main Chat Area -->
|
|
40
|
+
<div class="main">
|
|
41
|
+
<!-- Header -->
|
|
42
|
+
<div class="header">
|
|
43
|
+
<div class="header-left">
|
|
44
|
+
<button class="sidebar-toggle" id="sidebar-toggle" title="Toggle sidebar" aria-label="Toggle sidebar"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button>
|
|
45
|
+
<button class="model-input" id="model-input" type="button"
|
|
46
|
+
title="Choose model and (optionally) thinking level for this session"
|
|
47
|
+
aria-label="Choose model and thinking level"><span class="model-input-label" id="model-input-label">Model</span></button>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="header-right">
|
|
50
|
+
<button class="icon-btn hidden" id="tree-btn" title="Session tree" aria-label="Session tree">
|
|
51
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
|
|
52
|
+
</button>
|
|
53
|
+
<div class="stats-anchor">
|
|
54
|
+
<button class="pill context-pill" id="context-pill" type="button" title="Context usage — click for session stats" aria-haspopup="true" aria-expanded="false"></button>
|
|
55
|
+
<div class="stats-card hidden" id="session-stats-card"></div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="status">
|
|
58
|
+
<span class="status-indicator" id="status-indicator"></span>
|
|
59
|
+
<span class="status-text" id="status-text">Connecting...</span>
|
|
60
|
+
</div>
|
|
61
|
+
<button class="icon-btn" id="file-sidebar-toggle" title="Files" aria-label="Toggle file browser">
|
|
62
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
|
|
63
|
+
</button>
|
|
64
|
+
<button class="settings-btn" id="settings-btn" title="Settings" aria-label="Settings">
|
|
65
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<!-- In-page Tau live-session tabs -->
|
|
71
|
+
<div class="live-tabs" id="live-tabs">
|
|
72
|
+
<div class="live-tabs-list" id="live-tabs-list"></div>
|
|
73
|
+
<button class="live-tab-add" id="live-tab-add" title="New Tau tab" aria-label="New Tau tab">+</button>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<!-- Launcher (project picker) -->
|
|
77
|
+
<div class="launcher hidden" id="launcher"></div>
|
|
78
|
+
|
|
79
|
+
<!-- Messages Container -->
|
|
80
|
+
<div class="messages" id="messages">
|
|
81
|
+
<div class="welcome">
|
|
82
|
+
<div class="welcome-icon"><img src="icons/tau-192.png" alt="τ" class="tau-icon-welcome"></div>
|
|
83
|
+
<p>Welcome to Tau</p>
|
|
84
|
+
<p class="hint">Type a message below to start chatting with Pi, or select a session from the sidebar.</p>
|
|
85
|
+
<div class="shortcuts-hint">
|
|
86
|
+
<span>/ Focus input</span>
|
|
87
|
+
<span>Esc Abort</span>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- Scroll to bottom + new message indicator -->
|
|
93
|
+
<button class="scroll-bottom-btn hidden" id="scroll-bottom-btn">
|
|
94
|
+
<span class="scroll-bottom-badge hidden" id="scroll-bottom-badge">New <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg></span>
|
|
95
|
+
<span class="scroll-bottom-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg></span>
|
|
96
|
+
</button>
|
|
97
|
+
|
|
98
|
+
<!-- Typing indicator (hidden, kept for JS reference) -->
|
|
99
|
+
<div class="typing-indicator hidden" id="typing-indicator"></div>
|
|
100
|
+
|
|
101
|
+
<!-- Input Area -->
|
|
102
|
+
<div class="input-area">
|
|
103
|
+
<div class="queued-messages hidden" id="queued-messages"></div>
|
|
104
|
+
<div class="image-previews hidden" id="image-previews"></div>
|
|
105
|
+
<form id="chat-form">
|
|
106
|
+
<div class="input-left-actions">
|
|
107
|
+
<button type="button" class="input-icon-btn" id="command-btn" title="Commands" aria-label="Open commands" tabindex="-1">
|
|
108
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22v-5"/><path d="M9 8V2"/><path d="M15 8V2"/><path d="M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z"/></svg>
|
|
109
|
+
</button>
|
|
110
|
+
<button type="button" class="input-icon-btn" id="attach-btn" title="Attach image — images are uploaded as binary data. Only image files are supported. To reference local files, use the file browser sidebar." aria-label="Attach image" tabindex="-1">
|
|
111
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
|
|
112
|
+
</button>
|
|
113
|
+
|
|
114
|
+
<input type="file" id="image-input" accept="image/*" multiple style="display:none" />
|
|
115
|
+
</div>
|
|
116
|
+
<div class="input-bubble">
|
|
117
|
+
<textarea
|
|
118
|
+
id="message-input"
|
|
119
|
+
placeholder="Type a message... (Enter to send, Shift+Enter for newline)"
|
|
120
|
+
autocomplete="off"
|
|
121
|
+
rows="2"
|
|
122
|
+
|
|
123
|
+
></textarea>
|
|
124
|
+
<button type="button" class="input-mic-btn" id="mic-btn" title="Voice input" tabindex="-1">
|
|
125
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="input-actions">
|
|
129
|
+
<button type="submit" id="send-btn" title="Send message" tabindex="-1">
|
|
130
|
+
<span class="send-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg></span>
|
|
131
|
+
</button>
|
|
132
|
+
<button type="button" id="abort-btn" class="hidden" title="Abort (Esc)" tabindex="-1"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="2"/></svg></button>
|
|
133
|
+
</div>
|
|
134
|
+
</form>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<!-- File Browser (right sidebar) -->
|
|
139
|
+
<div class="file-sidebar collapsed" id="file-sidebar">
|
|
140
|
+
<div class="file-sidebar-header">
|
|
141
|
+
<span class="file-sidebar-title" id="file-sidebar-title">Files</span>
|
|
142
|
+
<button class="icon-btn" id="file-sidebar-up" title="Parent directory">
|
|
143
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 7-7 7 7"/><path d="M12 19V5"/></svg>
|
|
144
|
+
</button>
|
|
145
|
+
<button class="icon-btn" id="file-sidebar-finder" title="Open in file manager">
|
|
146
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"/></svg>
|
|
147
|
+
</button>
|
|
148
|
+
<button class="icon-btn" id="file-sidebar-close" title="Close" aria-label="Close file browser"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="file-sidebar-path" id="file-sidebar-path" title=""></div>
|
|
151
|
+
<div class="file-list" id="file-list"></div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Settings Overlay -->
|
|
156
|
+
<div class="settings-overlay hidden" id="settings-overlay"></div>
|
|
157
|
+
|
|
158
|
+
<!-- Settings Panel -->
|
|
159
|
+
<div class="settings-panel hidden" id="settings-panel">
|
|
160
|
+
<div class="settings-header">
|
|
161
|
+
<h3>Settings</h3>
|
|
162
|
+
<button class="settings-close" id="settings-close"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="settings-body">
|
|
165
|
+
<div class="settings-section">
|
|
166
|
+
<div class="settings-section-title">Appearance</div>
|
|
167
|
+
<div class="theme-grid" id="theme-grid"></div>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="settings-section">
|
|
170
|
+
<div class="settings-section-title">Agent</div>
|
|
171
|
+
<div class="settings-row" id="setting-auto-compact">
|
|
172
|
+
<span class="settings-label">Auto-compaction</span>
|
|
173
|
+
<button class="settings-toggle" id="toggle-auto-compact"></button>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="settings-row" id="setting-thinking">
|
|
176
|
+
<span class="settings-label">Thinking level</span>
|
|
177
|
+
<button class="settings-value-btn" id="btn-thinking-level"></button>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="settings-section">
|
|
181
|
+
<div class="settings-section-title">Display</div>
|
|
182
|
+
<div class="settings-row">
|
|
183
|
+
<span class="settings-label">Show thinking</span>
|
|
184
|
+
<button class="settings-toggle on" id="toggle-show-thinking"></button>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="settings-section" id="settings-auth-section" style="display:none">
|
|
188
|
+
<div class="settings-section-title">Authentication</div>
|
|
189
|
+
<div class="settings-row">
|
|
190
|
+
<span class="settings-label">Require login</span>
|
|
191
|
+
<button class="settings-toggle" id="toggle-auth"></button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<!-- Command Palette -->
|
|
198
|
+
<div class="command-palette-overlay hidden" id="command-palette-overlay"></div>
|
|
199
|
+
<div class="command-palette hidden" id="command-palette">
|
|
200
|
+
<div class="command-palette-header">Commands</div>
|
|
201
|
+
<div class="command-list" id="command-list"></div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<!-- Model Picker -->
|
|
205
|
+
<div class="model-picker-overlay hidden" id="model-picker-overlay"></div>
|
|
206
|
+
<div class="model-picker hidden" id="model-picker" role="dialog" aria-modal="true" aria-labelledby="model-picker-title">
|
|
207
|
+
<div class="model-picker-header">
|
|
208
|
+
<h3 id="model-picker-title">Model</h3>
|
|
209
|
+
<button class="model-picker-close" id="model-picker-close" type="button" aria-label="Close"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="model-picker-body">
|
|
212
|
+
<input class="model-picker-input" id="model-picker-input" type="text" placeholder="provider/model:thinking" autocomplete="off" spellcheck="false">
|
|
213
|
+
<div class="model-picker-message" id="model-picker-message">Type provider or model name; optional :off|minimal|low|medium|high|xhigh|max</div>
|
|
214
|
+
<div class="model-list" id="model-picker-list" role="listbox" aria-label="Available models"></div>
|
|
215
|
+
</div>
|
|
216
|
+
<div class="model-picker-actions">
|
|
217
|
+
<button type="button" class="secondary-btn" id="model-picker-cancel">Cancel</button>
|
|
218
|
+
<button type="button" class="primary-btn" id="model-picker-save">Save</button>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<!-- Session Tree -->
|
|
223
|
+
<div class="tree-view-overlay hidden" id="tree-view-overlay"></div>
|
|
224
|
+
<div class="tree-view hidden" id="tree-view" role="dialog" aria-modal="true" aria-labelledby="tree-view-title">
|
|
225
|
+
<div class="tree-view-header">
|
|
226
|
+
<h3 id="tree-view-title">Session tree</h3>
|
|
227
|
+
<button class="tree-view-filter" id="tree-view-filter" type="button" aria-pressed="false" title="Toggle tool results and housekeeping entries">Messages only</button>
|
|
228
|
+
<button class="model-picker-close" id="tree-view-close" type="button" aria-label="Close"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="tree-view-message" id="tree-view-message"></div>
|
|
231
|
+
<div class="tree-view-list" id="tree-view-list" role="listbox" aria-label="Session entries"></div>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<!-- New live session modal -->
|
|
235
|
+
<div class="new-live-session-overlay hidden" id="new-live-session-overlay"></div>
|
|
236
|
+
<div class="new-live-session-modal hidden" id="new-live-session-modal">
|
|
237
|
+
<div class="new-live-session-header">
|
|
238
|
+
<h3>New Tau Tab</h3>
|
|
239
|
+
<button class="settings-close" id="new-live-session-close" aria-label="Close"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
240
|
+
</div>
|
|
241
|
+
<form class="new-live-session-body" id="new-live-session-form">
|
|
242
|
+
<label class="new-live-session-label" for="new-live-session-cwd">Project directory</label>
|
|
243
|
+
<input class="new-live-session-input" id="new-live-session-cwd" type="text" placeholder="/path/to/project" autocomplete="off" required>
|
|
244
|
+
<div class="project-chips" id="new-live-session-projects"></div>
|
|
245
|
+
<label class="new-live-session-label" for="new-live-session-model">Model</label>
|
|
246
|
+
<input class="new-live-session-input" id="new-live-session-model" type="text" placeholder="openai/gpt-5.5:high" autocomplete="off">
|
|
247
|
+
<p class="new-live-session-help">Uses the same syntax as Pi <code>/model</code>; applies only to this tab. Leave blank for Pi default.</p>
|
|
248
|
+
<div class="new-live-session-actions">
|
|
249
|
+
<button type="button" class="secondary-btn" id="new-live-session-cancel">Cancel</button>
|
|
250
|
+
<button type="submit" class="primary-btn" id="new-live-session-submit">Create tab</button>
|
|
251
|
+
</div>
|
|
252
|
+
</form>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<!-- Dialog Container -->
|
|
256
|
+
<div id="dialog-container" class="hidden"></div>
|
|
257
|
+
|
|
258
|
+
<!-- Scripts -->
|
|
259
|
+
<!-- Deferred classic scripts run before module scripts in document order,
|
|
260
|
+
so window.katex is available by the time app.js executes. -->
|
|
261
|
+
<script defer src="vendor/katex/katex.min.js"></script>
|
|
262
|
+
<script type="module" src="app.js"></script>
|
|
263
|
+
</body>
|
|
264
|
+
</html>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Launcher } from './launcher.js';
|
|
2
|
+
export function setupLauncherPanel({ launcherEl, messagesContainer, createSession }) {
|
|
3
|
+
const launcher = new Launcher(launcherEl, createSession);
|
|
4
|
+
async function init() {
|
|
5
|
+
try {
|
|
6
|
+
const res = await fetch('/api/projects');
|
|
7
|
+
const data = await res.json();
|
|
8
|
+
if (data.projects && data.projects.length > 0) {
|
|
9
|
+
launcher.projects = data.projects;
|
|
10
|
+
launcher.render();
|
|
11
|
+
addLauncherNav();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
15
|
+
}
|
|
16
|
+
function addLauncherNav() {
|
|
17
|
+
const modeToggle = document.getElementById('mode-toggle');
|
|
18
|
+
if (!modeToggle || modeToggle.querySelector('.mode-link-launcher'))
|
|
19
|
+
return;
|
|
20
|
+
const launcherLink = document.createElement('span');
|
|
21
|
+
launcherLink.className = 'mode-link mode-link-launcher';
|
|
22
|
+
launcherLink.title = 'Projects';
|
|
23
|
+
launcherLink.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>';
|
|
24
|
+
launcherLink.addEventListener('click', () => show());
|
|
25
|
+
modeToggle.appendChild(launcherLink);
|
|
26
|
+
}
|
|
27
|
+
function isVisible() {
|
|
28
|
+
return !!launcherEl && !launcherEl.classList.contains('hidden');
|
|
29
|
+
}
|
|
30
|
+
function show() {
|
|
31
|
+
launcherEl.classList.remove('hidden');
|
|
32
|
+
messagesContainer.style.display = 'none';
|
|
33
|
+
const inputArea = document.querySelector('.input-area');
|
|
34
|
+
if (inputArea)
|
|
35
|
+
inputArea.style.display = 'none';
|
|
36
|
+
document.querySelector('.welcome')?.remove();
|
|
37
|
+
document.querySelectorAll('.mode-link').forEach(l => l.classList.remove('active'));
|
|
38
|
+
document.querySelector('.mode-link-launcher')?.classList.add('active');
|
|
39
|
+
launcher.load();
|
|
40
|
+
}
|
|
41
|
+
function hide() {
|
|
42
|
+
launcherEl.classList.add('hidden');
|
|
43
|
+
messagesContainer.style.display = '';
|
|
44
|
+
const inputArea = document.querySelector('.input-area');
|
|
45
|
+
if (inputArea)
|
|
46
|
+
inputArea.style.display = '';
|
|
47
|
+
document.querySelectorAll('.mode-link').forEach(l => l.classList.remove('active'));
|
|
48
|
+
document.querySelector('.mode-link:first-child')?.classList.add('active');
|
|
49
|
+
}
|
|
50
|
+
document.querySelector('.mode-link:first-child')?.addEventListener('click', () => {
|
|
51
|
+
hide();
|
|
52
|
+
});
|
|
53
|
+
return { init, isVisible, show, hide };
|
|
54
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launcher — project directory picker with visual bubbles
|
|
3
|
+
*/
|
|
4
|
+
export class Launcher {
|
|
5
|
+
container;
|
|
6
|
+
onLaunch;
|
|
7
|
+
projects;
|
|
8
|
+
constructor(container, onLaunch) {
|
|
9
|
+
this.container = container;
|
|
10
|
+
this.onLaunch = onLaunch;
|
|
11
|
+
this.projects = [];
|
|
12
|
+
}
|
|
13
|
+
async load() {
|
|
14
|
+
this.container.innerHTML = '<div class="launcher-loading">Loading projects…</div>';
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch('/api/projects');
|
|
17
|
+
const data = await res.json();
|
|
18
|
+
this.projects = data.projects || [];
|
|
19
|
+
this.render();
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
this.container.innerHTML = '<div class="launcher-loading">Failed to load projects</div>';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
render() {
|
|
26
|
+
if (!this.projects.length) {
|
|
27
|
+
this.container.innerHTML = `
|
|
28
|
+
<div class="launcher-empty">
|
|
29
|
+
<p>No projects directory configured.</p>
|
|
30
|
+
<p class="hint">Add <code>"tau": { "projectsDir": "~/Projects" }</code> to <code>~/.pi/agent/settings.json</code></p>
|
|
31
|
+
</div>`;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Find max session count for relative sizing
|
|
35
|
+
const maxSessions = Math.max(1, ...this.projects.map(p => p.sessionCount || 0));
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
// Sort: active first, then by recency
|
|
38
|
+
const sorted = [...this.projects].sort((a, b) => {
|
|
39
|
+
if (a.active && !b.active)
|
|
40
|
+
return -1;
|
|
41
|
+
if (!a.active && b.active)
|
|
42
|
+
return 1;
|
|
43
|
+
return (b.lastActive || 0) - (a.lastActive || 0);
|
|
44
|
+
});
|
|
45
|
+
const bubbles = sorted.map(p => {
|
|
46
|
+
// Size: scale between 0.7 and 1.3 based on session count
|
|
47
|
+
const sizeRatio = 0.7 + ((p.sessionCount || 0) / maxSessions) * 0.6;
|
|
48
|
+
// Recency: how fresh is this project (0 = ancient, 1 = today)
|
|
49
|
+
let freshness = 0;
|
|
50
|
+
if (p.lastActive) {
|
|
51
|
+
const ageMs = now - p.lastActive;
|
|
52
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
53
|
+
freshness = Math.max(0, 1 - (ageDays / 30)); // fades over 30 days
|
|
54
|
+
}
|
|
55
|
+
return `
|
|
56
|
+
<button class="launcher-bubble${p.active ? ' active' : ''}"
|
|
57
|
+
data-path="${this.escAttr(p.path)}"
|
|
58
|
+
style="--size: ${sizeRatio}; --freshness: ${freshness.toFixed(2)}"
|
|
59
|
+
title="${this.escAttr(p.path)}${p.active ? ' (running)' : ''}${p.sessionCount ? ` • ${p.sessionCount} session${p.sessionCount !== 1 ? 's' : ''}` : ''}">
|
|
60
|
+
<span class="launcher-bubble-name">${this.escHtml(p.name)}</span>
|
|
61
|
+
${p.active ? '<span class="launcher-bubble-dot"></span>' : ''}
|
|
62
|
+
</button>`;
|
|
63
|
+
}).join('');
|
|
64
|
+
this.container.innerHTML = `
|
|
65
|
+
<div class="launcher-content">
|
|
66
|
+
<div class="launcher-title">Projects</div>
|
|
67
|
+
<div class="launcher-grid">${bubbles}</div>
|
|
68
|
+
</div>`;
|
|
69
|
+
// Bind click handlers
|
|
70
|
+
this.container.querySelectorAll('.launcher-bubble').forEach(btn => {
|
|
71
|
+
btn.addEventListener('click', () => {
|
|
72
|
+
const projectPath = btn.dataset.path;
|
|
73
|
+
if (projectPath)
|
|
74
|
+
this.onLaunch(projectPath);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
escHtml(s) {
|
|
79
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
80
|
+
}
|
|
81
|
+
escAttr(s) {
|
|
82
|
+
return s.replace(/&/g, '&').replace(/"/g, '"');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Pi Tau Web Server",
|
|
3
|
+
"short_name": "Pi Tau",
|
|
4
|
+
"description": "Standalone web server for managing multiple Pi RPC sessions in the browser",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#f5f3ef",
|
|
8
|
+
"theme_color": "#f5f3ef",
|
|
9
|
+
"orientation": "any",
|
|
10
|
+
"icons": [
|
|
11
|
+
{
|
|
12
|
+
"src": "icons/tau-192.png",
|
|
13
|
+
"sizes": "192x192",
|
|
14
|
+
"type": "image/png"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"src": "icons/tau-512.png",
|
|
18
|
+
"sizes": "512x512",
|
|
19
|
+
"type": "image/png"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"src": "icons/tau-maskable-512.png",
|
|
23
|
+
"sizes": "512x512",
|
|
24
|
+
"type": "image/png",
|
|
25
|
+
"purpose": "maskable"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|