Skip to content

Commit 06b440b

Browse files
davindicodeclaude
andcommitted
Editor font size, scrollable paths, boxed close buttons, PDF zoom fix
- Code editor: aA font size controls (8-32), dynamic Monaco fontSize - File paths: horizontally scrollable + select-all for easy copying - Close buttons: boxed gray rounded style across all viewers - PDF viewer: proper zoom with centered dark background on zoom out - Monaco: enabled selection highlight, occurrences highlight, drag & drop - Added .env.example - Focus glow: upgraded from outline to box-shadow halo effect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 57dc707 commit 06b440b

6 files changed

Lines changed: 61 additions & 42 deletions

File tree

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
OTG_PORT=7777
2+
DEFAULT_SHELL=/bin/bash
3+
DEFAULT_CWD=/home/user

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ cp .env.example .env # or create manually:
3939
# DEFAULT_SHELL=/bin/bash
4040
# DEFAULT_CWD=/home/user
4141

42-
# Development
43-
pnpm dev
44-
45-
# Production with tunnel (one command)
42+
# Production with tunnel URL
4643
./start.sh
4744
```
4845

app/app.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,15 @@ body {
140140
.prose img { max-width: 100%; border-radius: 4px; }
141141
.prose hr { border: none; border-top: 1px solid #333; margin: 1.5em 0; }
142142

143-
/* Terminal focus indicator — light border when area is actively focused */
143+
/* Terminal focus indicator — glowing hue around active area */
144144
.terminal-focus-area {
145-
outline: 1px solid transparent;
146-
transition: outline-color 0.2s;
145+
border-radius: 4px;
146+
box-shadow: inset 0 0 0 1px transparent, 0 0 0 0 transparent;
147+
transition: box-shadow 0.3s ease;
147148
}
148149

149150
.terminal-focus-area:focus-within {
150-
outline-color: #569cd6;
151+
box-shadow: inset 0 0 0 1px rgba(86, 156, 214, 0.5), 0 0 12px rgba(86, 156, 214, 0.25), 0 0 4px rgba(86, 156, 214, 0.15);
151152
}
152153

153154
/* Global button press feedback — visual response on tap/click */

app/components/files/CodeEditor.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ export default function CodeEditor({ path, content, onSave, onClose }: CodeEdito
192192
const [value, setValue] = useState(content);
193193
const [dirty, setDirty] = useState(false);
194194
const [mode, setMode] = useState<"edit" | "preview">(canPreview ? "preview" : "edit");
195+
const [editorFontSize, setEditorFontSize] = useState(14);
195196

196197
const handleChange = (v: string | undefined) => {
197198
if (v !== undefined) {
@@ -208,9 +209,9 @@ export default function CodeEditor({ path, content, onSave, onClose }: CodeEdito
208209
return (
209210
<div className="flex flex-col h-full">
210211
{/* Toolbar */}
211-
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
212-
<span className="text-sm text-gray-300 truncate">{path}</span>
213-
<div className="flex items-center gap-2 shrink-0">
212+
<div className="flex items-center gap-2 px-3 py-1.5 bg-[#16162a] border-b border-gray-700 shrink-0">
213+
<span className="text-sm text-gray-300 overflow-x-auto scrollbar-none whitespace-nowrap min-w-0 flex-1 select-all">{path}</span>
214+
<div className="flex items-center gap-1.5 shrink-0">
214215
{canPreview && (
215216
<div className="flex items-center bg-[#0d0d1a] rounded overflow-hidden border border-gray-700">
216217
<button
@@ -235,27 +236,43 @@ export default function CodeEditor({ path, content, onSave, onClose }: CodeEdito
235236
</button>
236237
</div>
237238
)}
239+
{/* Font size */}
240+
<div className="flex items-center border border-gray-700 rounded overflow-hidden">
241+
<button
242+
onClick={() => setEditorFontSize((s) => Math.max(8, s - 1))}
243+
className="px-1.5 py-0.5 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors leading-none"
244+
>
245+
<span className="text-[9px] font-bold">a</span>
246+
</button>
247+
<span className="text-[10px] text-gray-500 w-5 text-center tabular-nums">{editorFontSize}</span>
248+
<button
249+
onClick={() => setEditorFontSize((s) => Math.min(32, s + 1))}
250+
className="px-1.5 py-0.5 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors leading-none"
251+
>
252+
<span className="text-[14px] font-bold">A</span>
253+
</button>
254+
</div>
238255
<a
239256
href={`/api/files/download?path=${encodeURIComponent(path)}`}
240257
download
241-
className="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-xs transition-colors"
258+
className="px-2 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-xs transition-colors"
242259
>
243260
Download
244261
</a>
245-
{dirty && <span className="text-xs text-yellow-500">Unsaved</span>}
262+
{dirty && <span className="text-xs text-yellow-500">*</span>}
246263
<button
247264
onClick={handleSave}
248265
disabled={!dirty}
249-
className="px-3 py-1 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-700 disabled:text-gray-500 text-white rounded text-xs font-medium transition-colors"
266+
className="px-2 py-1 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-700 disabled:text-gray-500 text-white rounded text-xs font-medium transition-colors"
250267
>
251268
Save
252269
</button>
253270
<button
254271
onClick={onClose}
255-
className="p-1 text-gray-400 hover:text-white transition-colors"
272+
className="p-1 bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white rounded transition-colors"
256273
title="Close"
257274
>
258-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
275+
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
259276
</button>
260277
</div>
261278
</div>
@@ -286,11 +303,14 @@ export default function CodeEditor({ path, content, onSave, onClose }: CodeEdito
286303
theme="vs-dark"
287304
options={{
288305
minimap: { enabled: false },
289-
fontSize: 14,
306+
fontSize: editorFontSize,
290307
wordWrap: "on",
291308
lineNumbers: "on",
292309
scrollBeyondLastLine: false,
293310
automaticLayout: true,
311+
selectionHighlight: true,
312+
occurrencesHighlight: "singleFile",
313+
dragAndDrop: true,
294314
}}
295315
/>
296316
</Suspense>

app/components/files/FileViewer.tsx

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function PdfViewer({ path, onClose }: { path: string; onClose: () => void }) {
4040
return (
4141
<div className="flex flex-col h-full">
4242
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
43-
<span className="text-sm text-gray-300 truncate">{path}</span>
43+
<span className="text-sm text-gray-300 overflow-x-auto scrollbar-none whitespace-nowrap min-w-0 flex-1 select-all">{path}</span>
4444
<div className="flex items-center gap-2">
4545
<div className="flex items-center gap-1 border border-gray-700 rounded overflow-hidden">
4646
<button onClick={zoomOut} className="px-2 py-0.5 text-xs text-gray-400 hover:text-white hover:bg-gray-700">-</button>
@@ -56,35 +56,33 @@ function PdfViewer({ path, onClose }: { path: string; onClose: () => void }) {
5656
</a>
5757
<button
5858
onClick={onClose}
59-
className="p-1 text-gray-400 hover:text-white transition-colors"
59+
className="p-1 bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white rounded transition-colors"
6060
title="Close"
6161
>
62-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
62+
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
6363
</button>
6464
</div>
6565
</div>
6666
<div
67-
className="flex-1 min-h-0 overflow-auto relative"
67+
className="flex-1 min-h-0 overflow-auto bg-[#0d0d1a] flex items-center justify-center"
6868
onTouchMove={handleTouchMove}
6969
onTouchEnd={handleTouchEnd}
7070
>
71-
{/* Spacer div sized to the scaled iframe dimensions so scrolling works */}
72-
<div style={{ width: `${100 * zoom}%`, height: `${100 * zoom}%`, pointerEvents: "none" }} />
73-
{/* Iframe scaled from top-left, positioned absolutely */}
74-
<iframe
75-
src={`/api/files/download?path=${encodeURIComponent(path)}&inline=1`}
76-
title={path}
71+
<div
7772
style={{
78-
position: "absolute",
79-
top: 0,
80-
left: 0,
81-
width: `${100 / zoom}%`,
82-
height: `${100 / zoom}%`,
83-
border: "none",
84-
transform: `scale(${zoom})`,
85-
transformOrigin: "top left",
73+
width: zoom <= 1 ? `${zoom * 100}%` : `${zoom * 100}%`,
74+
height: zoom <= 1 ? `${zoom * 100}%` : `${zoom * 100}%`,
75+
minWidth: zoom > 1 ? `${zoom * 100}%` : undefined,
76+
minHeight: zoom > 1 ? `${zoom * 100}%` : undefined,
77+
flexShrink: 0,
8678
}}
87-
/>
79+
>
80+
<iframe
81+
src={`/api/files/download?path=${encodeURIComponent(path)}&inline=1`}
82+
title={path}
83+
className="w-full h-full border-0"
84+
/>
85+
</div>
8886
</div>
8987
</div>
9088
);
@@ -109,7 +107,7 @@ export default function FileViewer({ path, content, onSave, onClose }: FileViewe
109107
return (
110108
<div className="flex flex-col h-full">
111109
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
112-
<span className="text-sm text-gray-300 truncate">{path}</span>
110+
<span className="text-sm text-gray-300 overflow-x-auto scrollbar-none whitespace-nowrap min-w-0 flex-1 select-all">{path}</span>
113111
<div className="flex items-center gap-2">
114112
<a
115113
href={`/api/files/download?path=${encodeURIComponent(path)}`}
@@ -120,10 +118,10 @@ export default function FileViewer({ path, content, onSave, onClose }: FileViewe
120118
</a>
121119
<button
122120
onClick={onClose}
123-
className="p-1 text-gray-400 hover:text-white transition-colors"
121+
className="p-1 bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white rounded transition-colors"
124122
title="Close"
125123
>
126-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
124+
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
127125
</button>
128126
</div>
129127
</div>

app/components/files/ImageViewer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default function ImageViewer({ path, onClose }: ImageViewerProps) {
3434
return (
3535
<div className="flex flex-col h-full">
3636
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
37-
<span className="text-sm text-gray-300 truncate">{path}</span>
37+
<span className="text-sm text-gray-300 overflow-x-auto scrollbar-none whitespace-nowrap min-w-0 flex-1 select-all">{path}</span>
3838
<div className="flex items-center gap-2 shrink-0">
3939
{/* Zoom controls */}
4040
<div className="flex items-center gap-1 border border-gray-700 rounded overflow-hidden">
@@ -51,10 +51,10 @@ export default function ImageViewer({ path, onClose }: ImageViewerProps) {
5151
</a>
5252
<button
5353
onClick={onClose}
54-
className="p-1 text-gray-400 hover:text-white transition-colors"
54+
className="p-1 bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white rounded transition-colors"
5555
title="Close"
5656
>
57-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
57+
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
5858
</button>
5959
</div>
6060
</div>

0 commit comments

Comments
 (0)