v1 (widget rebuild) was drafted against a stale snapshot. The live repo
already has the real filesystem, the Android APK, and a chunked buffer
(the 10 MB+ approach widget.Editor cannot do). The Android IME is
reachable from the op layer with only a small, precisely-defined gap:
missing key.SelectionCmd/SnippetCmd, an EditEvent handler that ignores
Range (corrupts swipe/autocorrect replacement commits), and a missing
InputHint. Completing those is now Phase 1; the emulator (now available
on the 16 GB VM) is the verification instrument.
NewLogic now takes (FileSystem, path, openfunc); update the e2e harness
and editor tests accordingly. The harness uses a no-op openfunc, matching
impl_other.go (the real app's OpenFile is a platform bridge, no-op off
Android).
Also adds doc/development_plan.md (draft, with corrections note re: the
live repo state).
- Add WriteFile method to mock filesystem (non-atomic path, creates files)
- Implement WriteFileAtomic in mock with temp file + rename pattern
using .tmp/ directory, matching real filesystem semantics
- Update WriteFileTask.Execute() to use WriteFileAtomic
- Update FlushAll() to use WriteFileAtomic
- Fix mock to create files on write (matching os.WriteFile behavior)
- Update tests to match new semantics (create-if-not-exists)
- Define pool.FileSystem interface in internal/io/pool/filesystem.go
- Move DirEntry interface to internal/io/pool/types/types.go to break
the pool <-> mock import cycle
- Implement real.FileSystem with atomic writes (temp file + os.Rename)
- Update mock.FileSystem to satisfy pool.FileSystem interface
- Update worker pool tasks to accept pool.FileSystem interface
- Update main.go to use RealFileSystem by default, with -root flag
- Update all browser/editor/test references to types.DirEntry
- Add Section 8 describing GlyphLayout data structure and feedback path
- Renderer captures byte offsets, X/Y positions, and advance widths during shaping
- Layout flows via layoutChan to logic goroutine, replacing lastLineYChan
- Editor uses layout for accurate cursor rendering and navigation
- Handles word wrap, screen resize, editing, scroll, and non-fixed-width fonts
- Replace gtx.Event(nil) with key.Filter{Focus: focusedID} and
key.FocusFilter{Target: focusedID} so Gio correctly routes key
and edit events to the focused editor text field.
- Convert EditorState.CursorPosition (byte offset) to line/column
coordinates for accurate cursor rendering.
- Add debug logging in HandleKeyDown and HandleCursorMove.
- Add detailed §15 lazy loading plan with 7 implementation steps
- Update Phase 2 checklist with specific tasks
- Add E2E test harness for browser files
- Refactor browser layout and handler structure
- Add .gitignore for common patterns
WIP: Actual lazy loading not yet implemented.
- Add clippableElement interface for elements that need their own clip region
- ListView implements NeedsClip() so drawElement pushes a clip before Draw
- RegisterScroll no longer has its own clip; relies on element clip context
- Aligns with Gio's clip-based gesture registration model
- Fix scroll events firing outside the visible list area on browser page
Also:
- Update touch.md documentation to reflect clip-based interaction model
- Remove debug logging from ListView.Draw
- Remove TODO-click-fixes.md (all items resolved)
- Add ui.Dp and ui.Px as distinct named types
- Logic layer works exclusively in Dp (positions, sizes, regions)
- Renderer converts Dp→Px at Gio interop boundaries
- Capture gtx.Constraints once at start of Draw() for consistent positioning
- main.go converts app.ConfigEvent pixels to Dp before passing to logic layer
- Update documentation with new coordinate system architecture
StatusBar (top bar):
- Icons re-arranged: [Cut] [Copy] [Paste] on left, [Conf] [Search] on right
- Removed Right/BytePosition field (moved to BottomBar)
BottomBar:
- Added BytePos field (e.g., "1024 / 50000 bytes")
- Word wrap is now a button (tap to toggle On/Off)
- Layout: CursorPos (left) | BytePos (center) | WordWrap button (right)
Element changes:
- StatusBar: removed Right field, added Search field
- BottomBar: added BytePos field, WordWrap is now a button
Interaction:
- Word wrap tap sends InputEvent{ElementID: "word_wrap"} to Logic
- Logic toggles word_wrap_enabled state field
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
StatusBar (top bar):
- Added Search icon (always visible, toggles search bar)
- Removed cursor position from top bar (moved to bottom bar)
- Fixed icon slots: Cut, Copy, Search, Paste (left to right)
- File size between Paste and Conflict icons
BottomBar (new element):
- Always visible at bottom of editor page (24 DP fixed height)
- Left: cursor position (Ln X, Col Y)
- Right: word wrap status (On/Off)
- Cursor position updated on every cursor movement
- Word wrap status persisted to .pad/state.json
Search button:
- Tap toggles search bar visible/invisible
- When active: shows SearchBar element, focuses text field, shows keyboard
- When inactive: hides SearchBar, clears query, hides keyboard
- Primary search activation method on Android (Ctrl+F still works as secondary)
Layout:
- Top: StatusBar (filename + action icons)
- Middle: SearchBar (when active) + TextField (editor)
- Bottom: BottomBar (cursor position + word wrap status)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Cut/Copy/Paste:
- Separate icons in StatusBar at fixed positions (no reflow)
- Clipboard persisted to .pad/clipboard.json (survives process death)
- Cut: copies to clipboard, deletes from buffer, appends to undo chain
- Copy: copies to clipboard, keeps text in buffer
- Paste: inserts clipboard text at cursor, appends to undo chain
- Visibility: Cut/Copy when selection exists, Paste when clipboard non-empty
Filename ellipsis toggle:
- Filename on separate line at top of StatusBar
- Truncated with ellipsis if too long
- Tap ellipsis toggles between truncated and full multi-line view
- filename_expanded state field (not persisted)
StatusBar layout:
- Line 1: filename (truncated or full)
- Line 2: [Cut] [Copy] Ln X, Col Y [Paste] size [Conflict]
- Fixed icon slots: Cut (left), Copy (36DP from Cut), Paste (right), Conflict (rightmost)
- Region.H computed dynamically based on filename expansion
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Update block diagram to show correct channel/mutex flow
- Frame receiver now calls w.Invalidate() inside the mutex lock
- Logic goroutine now waits on batched []InputEvent and configChan
- Main loop batches events and sends outside the lock
- Remove select-with-default batching from logic
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Line index: cached on disk in .pad/indices/, invalidated by mtime/size,
incremental updates during editing sessions
- Atomic writes: temp files live in .pad/tmp/, excluded from Syncthing via .stignore
- State storage: multi-file JSON approach (state.json, cursors.json, undo/*.json)
over SQLite — simpler, debuggable, sufficient for expected workload
- Undo limits: 1 MB inline cap on deleted field; larger deletions stored in
separate backup files; 10 MB total disk budget for undo directory
- Added search and word wrap sections to the editor spec
- Updated performance guarantees with search/jump targets
- Element is an interface with Region() and Visible() methods
- Concrete types have unexported region/visible/id fields
- Constructors (NewLabel, NewListView) replace embedded Element struct
- Theme simplified to FontSize only
- Removed OnSelect/OnPress callback IDs from element types
- Added internal/ui/ to architecture in spec.md
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Element interface with Region() and Visible() methods
- Label and ListView element types with constructors
- Renderer that clips and draws elements in slice order
- Main app loop with Gioui window and frame events
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>