aicodeman 0.2.9 → 0.3.1
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 +118 -4
- package/dist/ai-idle-checker.d.ts.map +1 -1
- package/dist/ai-idle-checker.js +3 -2
- package/dist/ai-idle-checker.js.map +1 -1
- package/dist/ai-plan-checker.d.ts.map +1 -1
- package/dist/ai-plan-checker.js +3 -2
- package/dist/ai-plan-checker.js.map +1 -1
- package/dist/bash-tool-parser.d.ts +2 -3
- package/dist/bash-tool-parser.d.ts.map +1 -1
- package/dist/bash-tool-parser.js +14 -31
- package/dist/bash-tool-parser.js.map +1 -1
- package/dist/config/ai-defaults.d.ts +16 -0
- package/dist/config/ai-defaults.d.ts.map +1 -0
- package/dist/config/ai-defaults.js +16 -0
- package/dist/config/ai-defaults.js.map +1 -0
- package/dist/config/auth-config.d.ts +19 -0
- package/dist/config/auth-config.d.ts.map +1 -0
- package/dist/config/auth-config.js +28 -0
- package/dist/config/auth-config.js.map +1 -0
- package/dist/config/exec-timeout.d.ts +10 -0
- package/dist/config/exec-timeout.d.ts.map +1 -0
- package/dist/config/exec-timeout.js +10 -0
- package/dist/config/exec-timeout.js.map +1 -0
- package/dist/config/map-limits.d.ts +4 -0
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +7 -0
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config/server-timing.d.ts +42 -0
- package/dist/config/server-timing.d.ts.map +1 -0
- package/dist/config/server-timing.js +57 -0
- package/dist/config/server-timing.js.map +1 -0
- package/dist/config/team-config.d.ts +16 -0
- package/dist/config/team-config.d.ts.map +1 -0
- package/dist/config/team-config.js +16 -0
- package/dist/config/team-config.js.map +1 -0
- package/dist/config/terminal-limits.d.ts +18 -0
- package/dist/config/terminal-limits.d.ts.map +1 -0
- package/dist/config/terminal-limits.js +18 -0
- package/dist/config/terminal-limits.js.map +1 -0
- package/dist/config/tunnel-config.d.ts +27 -0
- package/dist/config/tunnel-config.d.ts.map +1 -0
- package/dist/config/tunnel-config.js +36 -0
- package/dist/config/tunnel-config.js.map +1 -0
- package/dist/hooks-config.d.ts +21 -6
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +28 -12
- package/dist/hooks-config.js.map +1 -1
- package/dist/image-watcher.d.ts +4 -4
- package/dist/image-watcher.d.ts.map +1 -1
- package/dist/image-watcher.js +17 -30
- package/dist/image-watcher.js.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/plan-orchestrator.d.ts +2 -24
- package/dist/plan-orchestrator.d.ts.map +1 -1
- package/dist/plan-orchestrator.js.map +1 -1
- package/dist/prompts/planner.d.ts +7 -8
- package/dist/prompts/planner.d.ts.map +1 -1
- package/dist/prompts/planner.js +7 -8
- package/dist/prompts/planner.js.map +1 -1
- package/dist/prompts/research-agent.d.ts +6 -4
- package/dist/prompts/research-agent.d.ts.map +1 -1
- package/dist/prompts/research-agent.js +6 -4
- package/dist/prompts/research-agent.js.map +1 -1
- package/dist/push-store.d.ts +1 -1
- package/dist/push-store.d.ts.map +1 -1
- package/dist/push-store.js +4 -12
- package/dist/push-store.js.map +1 -1
- package/dist/ralph-fix-plan-watcher.d.ts +91 -0
- package/dist/ralph-fix-plan-watcher.d.ts.map +1 -0
- package/dist/ralph-fix-plan-watcher.js +326 -0
- package/dist/ralph-fix-plan-watcher.js.map +1 -0
- package/dist/ralph-loop.d.ts +14 -4
- package/dist/ralph-loop.d.ts.map +1 -1
- package/dist/ralph-loop.js +14 -4
- package/dist/ralph-loop.js.map +1 -1
- package/dist/ralph-plan-tracker.d.ts +201 -0
- package/dist/ralph-plan-tracker.d.ts.map +1 -0
- package/dist/ralph-plan-tracker.js +325 -0
- package/dist/ralph-plan-tracker.js.map +1 -0
- package/dist/ralph-stall-detector.d.ts +84 -0
- package/dist/ralph-stall-detector.d.ts.map +1 -0
- package/dist/ralph-stall-detector.js +139 -0
- package/dist/ralph-stall-detector.js.map +1 -0
- package/dist/ralph-status-parser.d.ts +141 -0
- package/dist/ralph-status-parser.d.ts.map +1 -0
- package/dist/ralph-status-parser.js +478 -0
- package/dist/ralph-status-parser.js.map +1 -0
- package/dist/ralph-tracker.d.ts +218 -692
- package/dist/ralph-tracker.d.ts.map +1 -1
- package/dist/ralph-tracker.js +389 -1723
- package/dist/ralph-tracker.js.map +1 -1
- package/dist/respawn-adaptive-timing.d.ts +61 -0
- package/dist/respawn-adaptive-timing.d.ts.map +1 -0
- package/dist/respawn-adaptive-timing.js +105 -0
- package/dist/respawn-adaptive-timing.js.map +1 -0
- package/dist/respawn-controller.d.ts +35 -115
- package/dist/respawn-controller.d.ts.map +1 -1
- package/dist/respawn-controller.js +167 -607
- package/dist/respawn-controller.js.map +1 -1
- package/dist/respawn-health.d.ts +54 -0
- package/dist/respawn-health.d.ts.map +1 -0
- package/dist/respawn-health.js +183 -0
- package/dist/respawn-health.js.map +1 -0
- package/dist/respawn-metrics.d.ts +81 -0
- package/dist/respawn-metrics.d.ts.map +1 -0
- package/dist/respawn-metrics.js +198 -0
- package/dist/respawn-metrics.js.map +1 -0
- package/dist/respawn-patterns.d.ts +45 -0
- package/dist/respawn-patterns.d.ts.map +1 -0
- package/dist/respawn-patterns.js +125 -0
- package/dist/respawn-patterns.js.map +1 -0
- package/dist/session-auto-ops.d.ts +89 -0
- package/dist/session-auto-ops.d.ts.map +1 -0
- package/dist/session-auto-ops.js +224 -0
- package/dist/session-auto-ops.js.map +1 -0
- package/dist/session-cli-builder.d.ts +62 -0
- package/dist/session-cli-builder.d.ts.map +1 -0
- package/dist/session-cli-builder.js +121 -0
- package/dist/session-cli-builder.js.map +1 -0
- package/dist/session-manager.d.ts +17 -5
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +17 -5
- package/dist/session-manager.js.map +1 -1
- package/dist/session-task-cache.d.ts +52 -0
- package/dist/session-task-cache.d.ts.map +1 -0
- package/dist/session-task-cache.js +90 -0
- package/dist/session-task-cache.js.map +1 -0
- package/dist/session.d.ts +23 -41
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +79 -317
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +19 -9
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +29 -30
- package/dist/state-store.js.map +1 -1
- package/dist/subagent-watcher.d.ts +26 -7
- package/dist/subagent-watcher.d.ts.map +1 -1
- package/dist/subagent-watcher.js +47 -64
- package/dist/subagent-watcher.js.map +1 -1
- package/dist/team-watcher.d.ts.map +1 -1
- package/dist/team-watcher.js +2 -5
- package/dist/team-watcher.js.map +1 -1
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +1 -2
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tunnel-manager.d.ts +26 -0
- package/dist/tunnel-manager.d.ts.map +1 -1
- package/dist/tunnel-manager.js +126 -7
- package/dist/tunnel-manager.js.map +1 -1
- package/dist/types/api.d.ts +108 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +98 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/app-state.d.ts +117 -0
- package/dist/types/app-state.d.ts.map +1 -0
- package/dist/types/app-state.js +76 -0
- package/dist/types/app-state.js.map +1 -0
- package/dist/types/common.d.ts +79 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +17 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/index.d.ts +66 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +66 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lifecycle.d.ts +28 -0
- package/dist/types/lifecycle.d.ts.map +1 -0
- package/dist/types/lifecycle.js +16 -0
- package/dist/types/lifecycle.js.map +1 -0
- package/dist/types/plan.d.ts +45 -0
- package/dist/types/plan.d.ts.map +1 -0
- package/dist/types/plan.js +18 -0
- package/dist/types/plan.js.map +1 -0
- package/dist/types/push.d.ts +36 -0
- package/dist/types/push.d.ts.map +1 -0
- package/dist/types/push.js +18 -0
- package/dist/types/push.js.map +1 -0
- package/dist/types/ralph.d.ts +262 -0
- package/dist/types/ralph.d.ts.map +1 -0
- package/dist/types/ralph.js +70 -0
- package/dist/types/ralph.js.map +1 -0
- package/dist/types/respawn.d.ts +271 -0
- package/dist/types/respawn.d.ts.map +1 -0
- package/dist/types/respawn.js +26 -0
- package/dist/types/respawn.js.map +1 -0
- package/dist/types/run-summary.d.ts +96 -0
- package/dist/types/run-summary.d.ts.map +1 -0
- package/dist/types/run-summary.js +37 -0
- package/dist/types/run-summary.js.map +1 -0
- package/dist/types/session.d.ts +152 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +27 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/task.d.ts +72 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +19 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/teams.d.ts +73 -0
- package/dist/types/teams.d.ts.map +1 -0
- package/dist/types/teams.js +23 -0
- package/dist/types/teams.js.map +1 -0
- package/dist/types/tools.d.ts +61 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +20 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/types.d.ts +8 -1134
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -210
- package/dist/types.js.map +1 -1
- package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
- package/dist/utils/claude-cli-resolver.js +1 -2
- package/dist/utils/claude-cli-resolver.js.map +1 -1
- package/dist/utils/debouncer.d.ts +111 -0
- package/dist/utils/debouncer.d.ts.map +1 -0
- package/dist/utils/debouncer.js +162 -0
- package/dist/utils/debouncer.js.map +1 -0
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
- package/dist/utils/opencode-cli-resolver.js +1 -2
- package/dist/utils/opencode-cli-resolver.js.map +1 -1
- package/dist/utils/string-similarity.d.ts +0 -57
- package/dist/utils/string-similarity.d.ts.map +1 -1
- package/dist/utils/string-similarity.js +3 -18
- package/dist/utils/string-similarity.js.map +1 -1
- package/dist/web/middleware/auth.d.ts +31 -0
- package/dist/web/middleware/auth.d.ts.map +1 -0
- package/dist/web/middleware/auth.js +154 -0
- package/dist/web/middleware/auth.js.map +1 -0
- package/dist/web/ports/auth-port.d.ts +18 -0
- package/dist/web/ports/auth-port.d.ts.map +1 -0
- package/dist/web/ports/auth-port.js +6 -0
- package/dist/web/ports/auth-port.js.map +1 -0
- package/dist/web/ports/config-port.d.ts +28 -0
- package/dist/web/ports/config-port.d.ts.map +1 -0
- package/dist/web/ports/config-port.js +6 -0
- package/dist/web/ports/config-port.js.map +1 -0
- package/dist/web/ports/event-port.d.ts +13 -0
- package/dist/web/ports/event-port.d.ts.map +1 -0
- package/dist/web/ports/event-port.js +6 -0
- package/dist/web/ports/event-port.js.map +1 -0
- package/dist/web/ports/index.d.ts +14 -0
- package/dist/web/ports/index.d.ts.map +1 -0
- package/dist/web/ports/index.js +9 -0
- package/dist/web/ports/index.js.map +1 -0
- package/dist/web/ports/infra-port.d.ts +36 -0
- package/dist/web/ports/infra-port.d.ts.map +1 -0
- package/dist/web/ports/infra-port.js +6 -0
- package/dist/web/ports/infra-port.js.map +1 -0
- package/dist/web/ports/respawn-port.d.ts +20 -0
- package/dist/web/ports/respawn-port.d.ts.map +1 -0
- package/dist/web/ports/respawn-port.js +6 -0
- package/dist/web/ports/respawn-port.js.map +1 -0
- package/dist/web/ports/session-port.d.ts +15 -0
- package/dist/web/ports/session-port.d.ts.map +1 -0
- package/dist/web/ports/session-port.js +6 -0
- package/dist/web/ports/session-port.js.map +1 -0
- package/dist/web/public/api-client.js +82 -0
- package/dist/web/public/api-client.js.br +0 -0
- package/dist/web/public/api-client.js.gz +0 -0
- package/dist/web/public/app.js +117 -201
- package/dist/web/public/app.js.br +0 -0
- package/dist/web/public/app.js.gz +0 -0
- package/dist/web/public/constants.js +365 -0
- package/dist/web/public/constants.js.br +0 -0
- package/dist/web/public/constants.js.gz +0 -0
- package/dist/web/public/index.html +15 -3
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/keyboard-accessory.js +302 -0
- package/dist/web/public/keyboard-accessory.js.br +0 -0
- package/dist/web/public/keyboard-accessory.js.gz +0 -0
- package/dist/web/public/mobile-handlers.js +491 -0
- package/dist/web/public/mobile-handlers.js.br +0 -0
- package/dist/web/public/mobile-handlers.js.gz +0 -0
- package/dist/web/public/mobile.css.gz +0 -0
- package/dist/web/public/notification-manager.js +472 -0
- package/dist/web/public/notification-manager.js.br +0 -0
- package/dist/web/public/notification-manager.js.gz +0 -0
- package/dist/web/public/ralph-wizard.js +33 -9
- package/dist/web/public/ralph-wizard.js.br +0 -0
- package/dist/web/public/ralph-wizard.js.gz +0 -0
- package/dist/web/public/styles.css.gz +0 -0
- package/dist/web/public/subagent-windows.js +1149 -0
- package/dist/web/public/subagent-windows.js.br +0 -0
- package/dist/web/public/subagent-windows.js.gz +0 -0
- package/dist/web/public/sw.js +15 -0
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.js +4 -0
- package/dist/web/public/vendor/xterm-zerolag-input.js.br +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.js +882 -0
- package/dist/web/public/voice-input.js.br +0 -0
- package/dist/web/public/voice-input.js.gz +0 -0
- package/dist/web/route-helpers.d.ts +38 -0
- package/dist/web/route-helpers.d.ts.map +1 -0
- package/dist/web/route-helpers.js +144 -0
- package/dist/web/route-helpers.js.map +1 -0
- package/dist/web/routes/case-routes.d.ts +9 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -0
- package/dist/web/routes/case-routes.js +426 -0
- package/dist/web/routes/case-routes.js.map +1 -0
- package/dist/web/routes/file-routes.d.ts +8 -0
- package/dist/web/routes/file-routes.d.ts.map +1 -0
- package/dist/web/routes/file-routes.js +337 -0
- package/dist/web/routes/file-routes.js.map +1 -0
- package/dist/web/routes/hook-event-routes.d.ts +9 -0
- package/dist/web/routes/hook-event-routes.d.ts.map +1 -0
- package/dist/web/routes/hook-event-routes.js +57 -0
- package/dist/web/routes/hook-event-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +16 -0
- package/dist/web/routes/index.d.ts.map +1 -0
- package/dist/web/routes/index.js +16 -0
- package/dist/web/routes/index.js.map +1 -0
- package/dist/web/routes/mux-routes.d.ts +8 -0
- package/dist/web/routes/mux-routes.d.ts.map +1 -0
- package/dist/web/routes/mux-routes.js +32 -0
- package/dist/web/routes/mux-routes.js.map +1 -0
- package/dist/web/routes/plan-routes.d.ts +9 -0
- package/dist/web/routes/plan-routes.d.ts.map +1 -0
- package/dist/web/routes/plan-routes.js +385 -0
- package/dist/web/routes/plan-routes.js.map +1 -0
- package/dist/web/routes/push-routes.d.ts +8 -0
- package/dist/web/routes/push-routes.d.ts.map +1 -0
- package/dist/web/routes/push-routes.js +49 -0
- package/dist/web/routes/push-routes.js.map +1 -0
- package/dist/web/routes/ralph-routes.d.ts +9 -0
- package/dist/web/routes/ralph-routes.d.ts.map +1 -0
- package/dist/web/routes/ralph-routes.js +485 -0
- package/dist/web/routes/ralph-routes.js.map +1 -0
- package/dist/web/routes/respawn-routes.d.ts +8 -0
- package/dist/web/routes/respawn-routes.d.ts.map +1 -0
- package/dist/web/routes/respawn-routes.js +270 -0
- package/dist/web/routes/respawn-routes.js.map +1 -0
- package/dist/web/routes/scheduled-routes.d.ts +8 -0
- package/dist/web/routes/scheduled-routes.d.ts.map +1 -0
- package/dist/web/routes/scheduled-routes.js +51 -0
- package/dist/web/routes/scheduled-routes.js.map +1 -0
- package/dist/web/routes/session-routes.d.ts +9 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -0
- package/dist/web/routes/session-routes.js +751 -0
- package/dist/web/routes/session-routes.js.map +1 -0
- package/dist/web/routes/system-routes.d.ts +9 -0
- package/dist/web/routes/system-routes.d.ts.map +1 -0
- package/dist/web/routes/system-routes.js +699 -0
- package/dist/web/routes/system-routes.js.map +1 -0
- package/dist/web/routes/team-routes.d.ts +8 -0
- package/dist/web/routes/team-routes.d.ts.map +1 -0
- package/dist/web/routes/team-routes.js +14 -0
- package/dist/web/routes/team-routes.js.map +1 -0
- package/dist/web/schemas.d.ts +43 -3
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +6 -2
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +35 -15
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +563 -3971
- package/dist/web/server.js.map +1 -1
- package/dist/web/sse-events.d.ts +361 -0
- package/dist/web/sse-events.d.ts.map +1 -0
- package/dist/web/sse-events.js +396 -0
- package/dist/web/sse-events.js.map +1 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +58 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mobile device support: detection, keyboard handling, and swipe navigation.
|
|
3
|
+
*
|
|
4
|
+
* Defines three singleton objects that manage mobile-specific behavior:
|
|
5
|
+
*
|
|
6
|
+
* - MobileDetection — Device type detection (mobile/tablet/desktop), touch capability,
|
|
7
|
+
* iOS/Safari identification, and body class management for CSS targeting.
|
|
8
|
+
* - KeyboardHandler — Virtual keyboard show/hide detection via visualViewport API,
|
|
9
|
+
* toolbar/accessory bar repositioning, terminal resize on keyboard open/close,
|
|
10
|
+
* and input scroll-into-view. Uses 100px threshold for iOS address bar drift.
|
|
11
|
+
* - SwipeHandler — Horizontal swipe detection on the terminal area for session switching.
|
|
12
|
+
* 80px minimum distance, 300ms maximum time, 100px max vertical drift.
|
|
13
|
+
*
|
|
14
|
+
* All three have init()/cleanup() lifecycle methods. They are re-initialized after SSE
|
|
15
|
+
* reconnect (in handleInit) to prevent stale closures.
|
|
16
|
+
*
|
|
17
|
+
* @globals {object} MobileDetection
|
|
18
|
+
* @globals {object} KeyboardHandler
|
|
19
|
+
* @globals {object} SwipeHandler
|
|
20
|
+
*
|
|
21
|
+
* @dependency keyboard-accessory.js (KeyboardAccessoryBar reference in KeyboardHandler.onKeyboardShow, soft — guarded with typeof check)
|
|
22
|
+
* @loadorder 2 of 9 — loaded after constants.js, before voice-input.js
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Codeman — Mobile detection, keyboard handling, and swipe navigation
|
|
26
|
+
// Loaded after constants.js, before app.js
|
|
27
|
+
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════
|
|
29
|
+
// Mobile Detection
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* MobileDetection - Detects device type and touch capability.
|
|
34
|
+
* Updates body classes for CSS targeting.
|
|
35
|
+
*/
|
|
36
|
+
const MobileDetection = {
|
|
37
|
+
/** Check if device supports touch input */
|
|
38
|
+
isTouchDevice() {
|
|
39
|
+
return 'ontouchstart' in window ||
|
|
40
|
+
navigator.maxTouchPoints > 0 ||
|
|
41
|
+
(window.matchMedia && window.matchMedia('(pointer: coarse)').matches);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/** Check if device is iOS (iPhone, iPad, iPod) */
|
|
45
|
+
isIOS() {
|
|
46
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
47
|
+
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/** Check if browser is Safari */
|
|
51
|
+
isSafari() {
|
|
52
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/** Check if screen is small (phone-sized, <430px) */
|
|
56
|
+
isSmallScreen() {
|
|
57
|
+
return window.innerWidth < 430;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/** Check if screen is medium (tablet-sized, 430-768px) */
|
|
61
|
+
isMediumScreen() {
|
|
62
|
+
return window.innerWidth >= 430 && window.innerWidth < 768;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/** Get device type based on screen width */
|
|
66
|
+
getDeviceType() {
|
|
67
|
+
const width = window.innerWidth;
|
|
68
|
+
if (width < 430) return 'mobile';
|
|
69
|
+
if (width < 768) return 'tablet';
|
|
70
|
+
return 'desktop';
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/** Update body classes based on device detection */
|
|
74
|
+
updateBodyClass() {
|
|
75
|
+
const body = document.body;
|
|
76
|
+
const deviceType = this.getDeviceType();
|
|
77
|
+
const isTouch = this.isTouchDevice();
|
|
78
|
+
|
|
79
|
+
// Remove existing device classes
|
|
80
|
+
body.classList.remove('device-mobile', 'device-tablet', 'device-desktop', 'touch-device', 'ios-device', 'safari-browser');
|
|
81
|
+
|
|
82
|
+
// Add current device class
|
|
83
|
+
body.classList.add(`device-${deviceType}`);
|
|
84
|
+
|
|
85
|
+
// Add touch device class if applicable
|
|
86
|
+
if (isTouch) {
|
|
87
|
+
body.classList.add('touch-device');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add iOS-specific class for safe area handling
|
|
91
|
+
if (this.isIOS()) {
|
|
92
|
+
body.classList.add('ios-device');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Add Safari class for browser-specific fixes
|
|
96
|
+
if (this.isSafari()) {
|
|
97
|
+
body.classList.add('safari-browser');
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/** Initialize mobile detection and set up resize listener */
|
|
102
|
+
init() {
|
|
103
|
+
this.updateBodyClass();
|
|
104
|
+
// Debounced resize handler
|
|
105
|
+
let resizeTimeout;
|
|
106
|
+
this._resizeHandler = () => {
|
|
107
|
+
clearTimeout(resizeTimeout);
|
|
108
|
+
resizeTimeout = setTimeout(() => this.updateBodyClass(), 100);
|
|
109
|
+
};
|
|
110
|
+
window.addEventListener('resize', this._resizeHandler);
|
|
111
|
+
|
|
112
|
+
// iOS: prevent pinch-to-zoom (Safari ignores user-scalable=no since iOS 10)
|
|
113
|
+
if (this.isIOS()) {
|
|
114
|
+
this._gestureStartHandler = (e) => e.preventDefault();
|
|
115
|
+
this._gestureChangeHandler = (e) => e.preventDefault();
|
|
116
|
+
document.addEventListener('gesturestart', this._gestureStartHandler);
|
|
117
|
+
document.addEventListener('gesturechange', this._gestureChangeHandler);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/** Remove event listeners */
|
|
122
|
+
cleanup() {
|
|
123
|
+
if (this._resizeHandler) {
|
|
124
|
+
window.removeEventListener('resize', this._resizeHandler);
|
|
125
|
+
this._resizeHandler = null;
|
|
126
|
+
}
|
|
127
|
+
if (this._gestureStartHandler) {
|
|
128
|
+
document.removeEventListener('gesturestart', this._gestureStartHandler);
|
|
129
|
+
document.removeEventListener('gesturechange', this._gestureChangeHandler);
|
|
130
|
+
this._gestureStartHandler = null;
|
|
131
|
+
this._gestureChangeHandler = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// ═══════════════════════════════════════════════════════════════
|
|
137
|
+
// Mobile Keyboard Handler
|
|
138
|
+
// ═══════════════════════════════════════════════════════════════
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* KeyboardHandler - Simple handler to scroll inputs into view when keyboard appears.
|
|
142
|
+
* Uses focusin event and scrollIntoView - keeps it simple and reliable.
|
|
143
|
+
* Also handles terminal scrolling and toolbar repositioning via visualViewport API.
|
|
144
|
+
*/
|
|
145
|
+
const KeyboardHandler = {
|
|
146
|
+
lastViewportHeight: 0,
|
|
147
|
+
keyboardVisible: false,
|
|
148
|
+
initialViewportHeight: 0,
|
|
149
|
+
|
|
150
|
+
/** Initialize keyboard handling */
|
|
151
|
+
init() {
|
|
152
|
+
// Only initialize on touch devices
|
|
153
|
+
if (!MobileDetection.isTouchDevice()) return;
|
|
154
|
+
|
|
155
|
+
this.initialViewportHeight = window.visualViewport?.height || window.innerHeight;
|
|
156
|
+
this.lastViewportHeight = this.initialViewportHeight;
|
|
157
|
+
|
|
158
|
+
// Simple focus handler - scroll input into view after keyboard appears
|
|
159
|
+
this._focusinHandler = (e) => {
|
|
160
|
+
const target = e.target;
|
|
161
|
+
if (!this.isInputElement(target)) return;
|
|
162
|
+
|
|
163
|
+
// Wait for keyboard animation, then scroll input into view
|
|
164
|
+
setTimeout(() => {
|
|
165
|
+
this.scrollInputIntoView(target);
|
|
166
|
+
}, 400);
|
|
167
|
+
};
|
|
168
|
+
document.addEventListener('focusin', this._focusinHandler);
|
|
169
|
+
|
|
170
|
+
// Use visualViewport to detect keyboard and reposition toolbar
|
|
171
|
+
if (window.visualViewport) {
|
|
172
|
+
this._viewportResizeHandler = () => {
|
|
173
|
+
this.handleViewportResize();
|
|
174
|
+
};
|
|
175
|
+
this._viewportScrollHandler = () => {
|
|
176
|
+
this.updateLayoutForKeyboard();
|
|
177
|
+
};
|
|
178
|
+
window.visualViewport.addEventListener('resize', this._viewportResizeHandler);
|
|
179
|
+
// Also handle scroll (iOS scrolls viewport when keyboard appears)
|
|
180
|
+
window.visualViewport.addEventListener('scroll', this._viewportScrollHandler);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/** Remove event listeners */
|
|
185
|
+
cleanup() {
|
|
186
|
+
if (this._focusinHandler) {
|
|
187
|
+
document.removeEventListener('focusin', this._focusinHandler);
|
|
188
|
+
this._focusinHandler = null;
|
|
189
|
+
}
|
|
190
|
+
if (this._viewportResizeHandler && window.visualViewport) {
|
|
191
|
+
window.visualViewport.removeEventListener('resize', this._viewportResizeHandler);
|
|
192
|
+
this._viewportResizeHandler = null;
|
|
193
|
+
}
|
|
194
|
+
if (this._viewportScrollHandler && window.visualViewport) {
|
|
195
|
+
window.visualViewport.removeEventListener('scroll', this._viewportScrollHandler);
|
|
196
|
+
this._viewportScrollHandler = null;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/** Handle viewport resize (keyboard show/hide) */
|
|
201
|
+
handleViewportResize() {
|
|
202
|
+
const currentHeight = window.visualViewport?.height || window.innerHeight;
|
|
203
|
+
const heightDiff = this.initialViewportHeight - currentHeight;
|
|
204
|
+
|
|
205
|
+
// Keyboard appeared (viewport shrunk by more than 150px)
|
|
206
|
+
if (heightDiff > 150 && !this.keyboardVisible) {
|
|
207
|
+
this.keyboardVisible = true;
|
|
208
|
+
document.body.classList.add('keyboard-visible');
|
|
209
|
+
this.onKeyboardShow();
|
|
210
|
+
}
|
|
211
|
+
// Keyboard hidden (viewport grew back close to initial)
|
|
212
|
+
// Use 100px threshold (not 50) to handle iOS address bar drift,
|
|
213
|
+
// iOS 26's persistent 24px discrepancy, and Safari bottom bar changes
|
|
214
|
+
else if (heightDiff < 100 && this.keyboardVisible) {
|
|
215
|
+
this.keyboardVisible = false;
|
|
216
|
+
document.body.classList.remove('keyboard-visible');
|
|
217
|
+
this.onKeyboardHide();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Update baseline when keyboard is not visible — adapts to address bar
|
|
221
|
+
// state changes, orientation changes, and other viewport shifts
|
|
222
|
+
if (!this.keyboardVisible) {
|
|
223
|
+
this.initialViewportHeight = currentHeight;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.updateLayoutForKeyboard();
|
|
227
|
+
this.lastViewportHeight = currentHeight;
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/** Update layout when keyboard shows/hides */
|
|
231
|
+
updateLayoutForKeyboard() {
|
|
232
|
+
if (!window.visualViewport) return;
|
|
233
|
+
|
|
234
|
+
// Only adjust on mobile
|
|
235
|
+
if (!MobileDetection.isSmallScreen() && !MobileDetection.isMediumScreen()) {
|
|
236
|
+
this.resetLayout();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const toolbar = document.querySelector('.toolbar');
|
|
241
|
+
const accessoryBar = document.querySelector('.keyboard-accessory-bar');
|
|
242
|
+
const main = document.querySelector('.main');
|
|
243
|
+
|
|
244
|
+
if (this.keyboardVisible) {
|
|
245
|
+
// Calculate keyboard offset
|
|
246
|
+
const layoutHeight = window.innerHeight;
|
|
247
|
+
const visualBottom = window.visualViewport.offsetTop + window.visualViewport.height;
|
|
248
|
+
const keyboardOffset = layoutHeight - visualBottom;
|
|
249
|
+
|
|
250
|
+
// Safety: if keyboard is supposedly visible but offset is 0 or negative,
|
|
251
|
+
// the keyboard is actually gone — force dismiss. This catches cases where
|
|
252
|
+
// visualViewport.resize fires late or with intermediate values on iOS.
|
|
253
|
+
if (keyboardOffset <= 0) {
|
|
254
|
+
this.keyboardVisible = false;
|
|
255
|
+
document.body.classList.remove('keyboard-visible');
|
|
256
|
+
this.onKeyboardHide();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Move toolbar up above keyboard
|
|
261
|
+
if (toolbar) {
|
|
262
|
+
toolbar.style.transform = `translateY(${-keyboardOffset}px)`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Move accessory bar up (it sits above toolbar)
|
|
266
|
+
if (accessoryBar) {
|
|
267
|
+
accessoryBar.style.transform = `translateY(${-keyboardOffset}px)`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Shrink main content area so terminal doesn't extend behind keyboard
|
|
271
|
+
// Account for keyboard height + toolbar height (40px) + accessory bar (44px)
|
|
272
|
+
if (main) {
|
|
273
|
+
main.style.paddingBottom = `${keyboardOffset + 94}px`;
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
this.resetLayout();
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
/** Reset layout to normal (no keyboard) */
|
|
281
|
+
resetLayout() {
|
|
282
|
+
const toolbar = document.querySelector('.toolbar');
|
|
283
|
+
const accessoryBar = document.querySelector('.keyboard-accessory-bar');
|
|
284
|
+
const main = document.querySelector('.main');
|
|
285
|
+
|
|
286
|
+
if (toolbar) {
|
|
287
|
+
toolbar.style.transform = '';
|
|
288
|
+
}
|
|
289
|
+
if (accessoryBar) {
|
|
290
|
+
accessoryBar.style.transform = '';
|
|
291
|
+
}
|
|
292
|
+
if (main) {
|
|
293
|
+
main.style.paddingBottom = '';
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/** Called when keyboard appears */
|
|
298
|
+
onKeyboardShow() {
|
|
299
|
+
// Show keyboard accessory bar
|
|
300
|
+
if (typeof KeyboardAccessoryBar !== 'undefined') {
|
|
301
|
+
KeyboardAccessoryBar.show();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Refit terminal locally AND send resize to server so Claude Code (Ink)
|
|
305
|
+
// knows the actual terminal dimensions. Without this, Ink redraws at the
|
|
306
|
+
// old (larger) row count when the user types, causing content to scroll
|
|
307
|
+
// off the visible area with each keystroke.
|
|
308
|
+
// Note: the throttledResize handler still suppresses ongoing resize events
|
|
309
|
+
// while keyboard is up — this one-shot resize on open/close is sufficient.
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
if (typeof app !== 'undefined' && app.terminal) {
|
|
312
|
+
if (app.fitAddon) try { app.fitAddon.fit(); } catch {}
|
|
313
|
+
app.terminal.scrollToBottom();
|
|
314
|
+
// Send resize to server so PTY dimensions match xterm
|
|
315
|
+
this._sendTerminalResize();
|
|
316
|
+
}
|
|
317
|
+
}, 150);
|
|
318
|
+
|
|
319
|
+
// Reposition subagent windows to stack from bottom (above keyboard)
|
|
320
|
+
if (typeof app !== 'undefined') app.relayoutMobileSubagentWindows();
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/** Called when keyboard hides */
|
|
324
|
+
onKeyboardHide() {
|
|
325
|
+
// Hide keyboard accessory bar
|
|
326
|
+
if (typeof KeyboardAccessoryBar !== 'undefined') {
|
|
327
|
+
KeyboardAccessoryBar.hide();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.resetLayout();
|
|
331
|
+
|
|
332
|
+
// Refit terminal, scroll to bottom, and send resize to restore original dimensions
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
if (typeof app !== 'undefined' && app.fitAddon) {
|
|
335
|
+
try { app.fitAddon.fit(); } catch {}
|
|
336
|
+
if (app.terminal) app.terminal.scrollToBottom();
|
|
337
|
+
// Send resize to server to restore full terminal size
|
|
338
|
+
this._sendTerminalResize();
|
|
339
|
+
}
|
|
340
|
+
}, 100);
|
|
341
|
+
|
|
342
|
+
// Reposition subagent windows to stack from top (below header)
|
|
343
|
+
if (typeof app !== 'undefined') app.relayoutMobileSubagentWindows();
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
/** Send current terminal dimensions to the server (one-shot, for keyboard open/close) */
|
|
347
|
+
_sendTerminalResize() {
|
|
348
|
+
if (typeof app === 'undefined' || !app.activeSessionId || !app.fitAddon) return;
|
|
349
|
+
try {
|
|
350
|
+
const dims = app.fitAddon.proposeDimensions();
|
|
351
|
+
if (dims) {
|
|
352
|
+
const cols = Math.max(dims.cols, 40);
|
|
353
|
+
const rows = Math.max(dims.rows, 10);
|
|
354
|
+
app._lastResizeDims = { cols, rows };
|
|
355
|
+
fetch(`/api/sessions/${app.activeSessionId}/resize`, {
|
|
356
|
+
method: 'POST',
|
|
357
|
+
headers: { 'Content-Type': 'application/json' },
|
|
358
|
+
body: JSON.stringify({ cols, rows })
|
|
359
|
+
}).catch(() => {});
|
|
360
|
+
}
|
|
361
|
+
} catch {}
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
/** Check if element is an input that triggers keyboard (excludes terminal) */
|
|
365
|
+
isInputElement(el) {
|
|
366
|
+
if (!el) return false;
|
|
367
|
+
|
|
368
|
+
// Exclude xterm.js terminal inputs (they handle their own scroll)
|
|
369
|
+
if (el.closest('.xterm') || el.closest('.terminal-container')) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const tagName = el.tagName?.toLowerCase();
|
|
374
|
+
// Exclude type=range, type=checkbox, type=radio (don't trigger keyboard)
|
|
375
|
+
if (tagName === 'input') {
|
|
376
|
+
const type = el.type?.toLowerCase();
|
|
377
|
+
if (type === 'checkbox' || type === 'radio' || type === 'range' || type === 'file') {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return (
|
|
382
|
+
tagName === 'input' ||
|
|
383
|
+
tagName === 'textarea' ||
|
|
384
|
+
el.isContentEditable
|
|
385
|
+
);
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/** Scroll input into view above the keyboard */
|
|
389
|
+
scrollInputIntoView(input) {
|
|
390
|
+
// Check if input is still focused (user might have tapped away)
|
|
391
|
+
if (document.activeElement !== input) return;
|
|
392
|
+
|
|
393
|
+
// Find if we're in a modal
|
|
394
|
+
const modal = input.closest('.modal.active');
|
|
395
|
+
const modalBody = modal?.querySelector('.modal-body');
|
|
396
|
+
|
|
397
|
+
if (modalBody) {
|
|
398
|
+
// For modals - scroll within the modal body
|
|
399
|
+
const inputRect = input.getBoundingClientRect();
|
|
400
|
+
const modalRect = modalBody.getBoundingClientRect();
|
|
401
|
+
|
|
402
|
+
// If input is below middle of modal, scroll it up
|
|
403
|
+
if (inputRect.top > modalRect.top + modalRect.height * 0.4) {
|
|
404
|
+
const scrollAmount = inputRect.top - modalRect.top - 100;
|
|
405
|
+
modalBody.scrollBy({ top: scrollAmount, behavior: 'smooth' });
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
// For page-level - use scrollIntoView
|
|
409
|
+
input.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// ═══════════════════════════════════════════════════════════════
|
|
415
|
+
// Mobile Swipe Handler
|
|
416
|
+
// ═══════════════════════════════════════════════════════════════
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* SwipeHandler - Detects horizontal swipes on terminal to switch sessions.
|
|
420
|
+
* Only active on mobile/touch devices.
|
|
421
|
+
*/
|
|
422
|
+
const SwipeHandler = {
|
|
423
|
+
startX: 0,
|
|
424
|
+
startY: 0,
|
|
425
|
+
startTime: 0,
|
|
426
|
+
minSwipeDistance: 80, // Minimum pixels for a valid swipe
|
|
427
|
+
maxSwipeTime: 300, // Maximum ms for a swipe gesture
|
|
428
|
+
maxVerticalDrift: 100, // Max vertical movement allowed
|
|
429
|
+
|
|
430
|
+
_touchStartHandler: null,
|
|
431
|
+
_touchEndHandler: null,
|
|
432
|
+
_element: null,
|
|
433
|
+
|
|
434
|
+
/** Initialize swipe handling */
|
|
435
|
+
init() {
|
|
436
|
+
// Only on touch devices
|
|
437
|
+
if (!MobileDetection.isTouchDevice()) return;
|
|
438
|
+
|
|
439
|
+
const terminal = document.querySelector('.main');
|
|
440
|
+
if (!terminal) return;
|
|
441
|
+
|
|
442
|
+
this._element = terminal;
|
|
443
|
+
this._touchStartHandler = (e) => this.onTouchStart(e);
|
|
444
|
+
this._touchEndHandler = (e) => this.onTouchEnd(e);
|
|
445
|
+
terminal.addEventListener('touchstart', this._touchStartHandler, { passive: true });
|
|
446
|
+
terminal.addEventListener('touchend', this._touchEndHandler, { passive: true });
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
/** Remove swipe listeners */
|
|
450
|
+
cleanup() {
|
|
451
|
+
if (this._element && this._touchStartHandler) {
|
|
452
|
+
this._element.removeEventListener('touchstart', this._touchStartHandler);
|
|
453
|
+
this._element.removeEventListener('touchend', this._touchEndHandler);
|
|
454
|
+
}
|
|
455
|
+
this._touchStartHandler = null;
|
|
456
|
+
this._touchEndHandler = null;
|
|
457
|
+
this._element = null;
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
onTouchStart(e) {
|
|
461
|
+
if (!e.touches || e.touches.length !== 1) return;
|
|
462
|
+
this.startX = e.touches[0].clientX;
|
|
463
|
+
this.startY = e.touches[0].clientY;
|
|
464
|
+
this.startTime = Date.now();
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
onTouchEnd(e) {
|
|
468
|
+
if (!e.changedTouches || e.changedTouches.length !== 1) return;
|
|
469
|
+
|
|
470
|
+
const endX = e.changedTouches[0].clientX;
|
|
471
|
+
const endY = e.changedTouches[0].clientY;
|
|
472
|
+
const elapsed = Date.now() - this.startTime;
|
|
473
|
+
|
|
474
|
+
// Check if it's a valid swipe
|
|
475
|
+
const deltaX = endX - this.startX;
|
|
476
|
+
const deltaY = Math.abs(endY - this.startY);
|
|
477
|
+
|
|
478
|
+
if (elapsed > this.maxSwipeTime) return; // Too slow
|
|
479
|
+
if (deltaY > this.maxVerticalDrift) return; // Too much vertical movement
|
|
480
|
+
if (Math.abs(deltaX) < this.minSwipeDistance) return; // Too short
|
|
481
|
+
|
|
482
|
+
// Valid swipe detected
|
|
483
|
+
if (deltaX > 0) {
|
|
484
|
+
// Swipe right -> previous session
|
|
485
|
+
if (typeof app !== 'undefined') app.prevSession();
|
|
486
|
+
} else {
|
|
487
|
+
// Swipe left -> next session
|
|
488
|
+
if (typeof app !== 'undefined') app.nextSession();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|