Skip to content

Commit 02d3b3f

Browse files
davindicodeclaude
andcommitted
Fix responsive layout, image viewer zoom, file path selection, bump to pre-alpha
- Desktop layout now requires landscape+768px or 1024px+, so portrait tablets get the mobile single-panel UI with tab bar - Image viewer uses actual width/height instead of CSS transform scale, fixing clipped edges when zoomed in — full pan/scroll at any zoom level - File path text in viewers is now selectable and horizontally scrollable instead of truncated with ellipsis and select-none - Version bumped to 0.1.0-pre-alpha Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 93baae9 commit 02d3b3f

6 files changed

Lines changed: 32 additions & 10 deletions

File tree

app/components/AppShell.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ import BrowserPage from "./browser/BrowserPage";
88
import InputBox from "./terminal/InputBox";
99
import { useUiStore } from "~/stores/uiStore";
1010

11+
// Desktop layout requires landscape orientation AND at least 768px width,
12+
// OR at least 1024px width in any orientation.
13+
// This ensures portrait tablets get the mobile single-panel UI.
14+
const DESKTOP_QUERY = "(min-width: 1024px), (min-width: 768px) and (orientation: landscape)";
15+
1116
function useIsDesktop() {
1217
const [isDesktop, setIsDesktop] = useState(
13-
typeof window !== "undefined" ? window.innerWidth >= 768 : true
18+
typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
1419
);
1520
useEffect(() => {
16-
const mq = window.matchMedia("(min-width: 768px)");
21+
const mq = window.matchMedia(DESKTOP_QUERY);
1722
const handler = (e: MediaQueryListEvent) => setIsDesktop(e.matches);
1823
mq.addEventListener("change", handler);
1924
return () => mq.removeEventListener("change", handler);

app/components/MobileTabBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function MobileTabBar() {
3535
const setActiveTab = useUiStore((s) => s.setActiveTab);
3636

3737
return (
38-
<nav className="flex items-center border-t border-gray-800 bg-[#0d0d1a] shrink-0 md:hidden safe-bottom">
38+
<nav className="flex items-center border-t border-gray-800 bg-[#0d0d1a] shrink-0 safe-bottom">
3939
{tabs.map((tab) => (
4040
<button
4141
key={tab.id}

app/components/files/CodeEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ export default function CodeEditor({ path, content, onSave, onClose }: CodeEdito
223223
<div className="flex flex-col h-full">
224224
{/* Toolbar */}
225225
<div className="flex items-center gap-2 px-3 py-1.5 bg-[#16162a] border-b border-gray-700 shrink-0">
226-
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-hidden text-ellipsis select-none mr-2" title={path}>{path}</span>
226+
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-x-auto select-text mr-2 scrollbar-none" title={path}>{path}</span>
227227
<div className="flex items-center gap-1.5 shrink-0">
228228
{canPreview && (
229229
<div className="flex items-center bg-[#0d0d1a] rounded overflow-hidden border border-gray-700">

app/components/files/FileViewer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function MediaViewer({ path, type, onClose }: { path: string; type: "video" | "a
2727
return (
2828
<div className="flex flex-col h-full">
2929
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
30-
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-hidden text-ellipsis select-none mr-2" title={path}>{path}</span>
30+
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-x-auto select-text mr-2 scrollbar-none" title={path}>{path}</span>
3131
<div className="flex items-center gap-2 shrink-0">
3232
<a
3333
href={`/api/files/download?path=${encodeURIComponent(path)}`}
@@ -75,7 +75,7 @@ function PdfViewer({ path, onClose }: { path: string; onClose: () => void }) {
7575
return (
7676
<div className="flex flex-col h-full">
7777
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
78-
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-hidden text-ellipsis select-none mr-2" title={path}>{path}</span>
78+
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-x-auto select-text mr-2 scrollbar-none" title={path}>{path}</span>
7979
<div className="flex items-center gap-2 shrink-0">
8080
{numPages > 0 && (
8181
<span className="text-[10px] text-gray-500">{numPages} pg</span>
@@ -158,7 +158,7 @@ export default function FileViewer({ path, content, onSave, onClose }: FileViewe
158158
return (
159159
<div className="flex flex-col h-full">
160160
<div className="flex items-center justify-between px-3 py-2 bg-[#16162a] border-b border-gray-700 shrink-0">
161-
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-hidden text-ellipsis select-none mr-2" title={path}>{path}</span>
161+
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-x-auto select-text mr-2 scrollbar-none" title={path}>{path}</span>
162162
<div className="flex items-center gap-2 shrink-0">
163163
<a
164164
href={`/api/files/download?path=${encodeURIComponent(path)}`}

app/components/files/ImageViewer.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ interface ImageViewerProps {
77

88
export default function ImageViewer({ path, onClose }: ImageViewerProps) {
99
const [zoom, setZoom] = useState(1);
10+
const [naturalSize, setNaturalSize] = useState<{ w: number; h: number } | null>(null);
1011
const containerRef = useRef<HTMLDivElement>(null);
1112

1213
const zoomIn = () => setZoom((z) => Math.min(5, z + 0.25));
1314
const zoomOut = () => setZoom((z) => Math.max(0.1, z - 0.25));
1415
const zoomFit = () => setZoom(1);
1516

17+
const handleImageLoad = useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {
18+
const img = e.currentTarget;
19+
setNaturalSize({ w: img.naturalWidth, h: img.naturalHeight });
20+
}, []);
21+
1622
// Pinch-to-zoom
1723
const lastDistance = useRef(0);
1824
const handleTouchMove = useCallback((e: React.TouchEvent) => {
@@ -34,7 +40,7 @@ export default function ImageViewer({ path, onClose }: ImageViewerProps) {
3440
return (
3541
<div className="flex flex-col h-full">
3642
<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 whitespace-nowrap min-w-0 flex-1 overflow-hidden text-ellipsis select-none mr-2" title={path}>{path}</span>
43+
<span className="text-sm text-gray-300 whitespace-nowrap min-w-0 flex-1 overflow-x-auto select-text mr-2 scrollbar-none" title={path}>{path}</span>
3844
<div className="flex items-center gap-2 shrink-0">
3945
{/* Zoom controls */}
4046
<div className="flex items-center gap-1 border border-gray-700 rounded overflow-hidden">
@@ -65,12 +71,23 @@ export default function ImageViewer({ path, onClose }: ImageViewerProps) {
6571
onTouchMove={handleTouchMove}
6672
onTouchEnd={handleTouchEnd}
6773
>
68-
<div className="min-w-full min-h-full flex items-center justify-center p-4" style={{ transform: `scale(${zoom})`, transformOrigin: "center center" }}>
74+
<div
75+
className="flex items-center justify-center p-4"
76+
style={{
77+
minWidth: naturalSize ? Math.max(naturalSize.w * zoom + 32, containerRef.current?.clientWidth ?? 0) : "100%",
78+
minHeight: naturalSize ? Math.max(naturalSize.h * zoom + 32, containerRef.current?.clientHeight ?? 0) : "100%",
79+
}}
80+
>
6981
<img
7082
src={`/api/files/download?path=${encodeURIComponent(path)}&inline=1`}
7183
alt={path}
7284
className="max-w-none"
7385
draggable={false}
86+
onLoad={handleImageLoad}
87+
style={naturalSize ? {
88+
width: naturalSize.w * zoom,
89+
height: naturalSize.h * zoom,
90+
} : undefined}
7491
/>
7592
</div>
7693
</div>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "otgcode",
3-
"version": "0.1.0",
3+
"version": "0.1.0-pre-alpha",
44
"private": true,
55
"type": "module",
66
"scripts": {

0 commit comments

Comments
 (0)