painapple-code 1.0.0__py3-none-any.whl
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.
- painapple_code/__init__.py +30 -0
- painapple_code/__main__.py +18 -0
- painapple_code/_version.py +24 -0
- painapple_code/auth_middleware.py +930 -0
- painapple_code/cli/__init__.py +204 -0
- painapple_code/cli/deploy/__init__.py +15 -0
- painapple_code/cli/deploy/claude_seed.py +41 -0
- painapple_code/cli/deploy/config.py +260 -0
- painapple_code/cli/deploy/container.py +715 -0
- painapple_code/cli/deploy/launch.py +163 -0
- painapple_code/cli/deploy/runtime.py +223 -0
- painapple_code/cli/lifecycle_cmd.py +443 -0
- painapple_code/cli/list_cmd.py +572 -0
- painapple_code/cli/manage_cmd.py +670 -0
- painapple_code/cli/netinfo.py +125 -0
- painapple_code/cli/profiles.py +390 -0
- painapple_code/cli/serve_args.py +163 -0
- painapple_code/cli/serve_config.py +256 -0
- painapple_code/cli/setup_cmd.py +664 -0
- painapple_code/cli/ui.py +438 -0
- painapple_code/cost_analytics.py +879 -0
- painapple_code/data/models.defaults.yaml +18 -0
- painapple_code/data/models.yaml +18 -0
- painapple_code/data/presets.defaults.json +32 -0
- painapple_code/data/strings.yaml +2269 -0
- painapple_code/helpers.py +406 -0
- painapple_code/paths.py +1514 -0
- painapple_code/prompt_explorer.py +984 -0
- painapple_code/providers/__init__.py +224 -0
- painapple_code/providers/base.py +710 -0
- painapple_code/providers/claude/__init__.py +74 -0
- painapple_code/providers/claude/capabilities.py +218 -0
- painapple_code/providers/claude/errors.py +79 -0
- painapple_code/providers/claude/launch.py +50 -0
- painapple_code/providers/claude/summary.py +89 -0
- painapple_code/providers/claude/translate.py +74 -0
- painapple_code/providers/claude_sdk/__init__.py +139 -0
- painapple_code/providers/claude_sdk/driver.py +412 -0
- painapple_code/providers/codex/__init__.py +87 -0
- painapple_code/providers/codex/capabilities.py +321 -0
- painapple_code/providers/codex/errors.py +70 -0
- painapple_code/providers/codex/launch.py +142 -0
- painapple_code/providers/codex/summary.py +196 -0
- painapple_code/providers/codex/translate.py +204 -0
- painapple_code/providers/codex_app_server/__init__.py +100 -0
- painapple_code/providers/codex_app_server/capabilities.py +41 -0
- painapple_code/providers/codex_app_server/launch.py +202 -0
- painapple_code/providers/codex_app_server/summary.py +291 -0
- painapple_code/providers/codex_app_server/translate.py +263 -0
- painapple_code/providers/codex_app_server/transport.py +238 -0
- painapple_code/routes/__init__.py +0 -0
- painapple_code/routes/api_agents.py +441 -0
- painapple_code/routes/api_app.py +368 -0
- painapple_code/routes/api_app_commit_sections.py +127 -0
- painapple_code/routes/api_app_config.py +997 -0
- painapple_code/routes/api_app_session_prefs.py +217 -0
- painapple_code/routes/api_browser.py +690 -0
- painapple_code/routes/api_commands.py +374 -0
- painapple_code/routes/api_costs.py +128 -0
- painapple_code/routes/api_drafts.py +139 -0
- painapple_code/routes/api_exec.py +149 -0
- painapple_code/routes/api_files.py +542 -0
- painapple_code/routes/api_git.py +657 -0
- painapple_code/routes/api_logs.py +649 -0
- painapple_code/routes/api_plugins.py +275 -0
- painapple_code/routes/api_project_config.py +418 -0
- painapple_code/routes/api_prompts.py +231 -0
- painapple_code/routes/api_providers.py +53 -0
- painapple_code/routes/api_search.py +411 -0
- painapple_code/routes/api_session_stash.py +234 -0
- painapple_code/routes/api_session_welcome.py +151 -0
- painapple_code/routes/api_sessions.py +742 -0
- painapple_code/routes/api_shadow.py +323 -0
- painapple_code/routes/api_shadow_db.py +512 -0
- painapple_code/routes/api_shadow_files.py +332 -0
- painapple_code/routes/api_shadow_search.py +476 -0
- painapple_code/routes/api_skills.py +610 -0
- painapple_code/routes/api_tasks.py +147 -0
- painapple_code/routes/api_terminal.py +540 -0
- painapple_code/routes/api_upload.py +293 -0
- painapple_code/routes/api_viewer.py +229 -0
- painapple_code/routes/dependencies.py +109 -0
- painapple_code/routes/ws_chat.py +837 -0
- painapple_code/server.py +2208 -0
- painapple_code/server_logging.py +263 -0
- painapple_code/services/__init__.py +0 -0
- painapple_code/services/agent_session.py +2738 -0
- painapple_code/session_store.py +253 -0
- painapple_code/session_store_core.py +879 -0
- painapple_code/shadow_db.py +830 -0
- painapple_code/shadow_db_plans.py +369 -0
- painapple_code/shadow_db_queries.py +595 -0
- painapple_code/shadow_db_schema.py +676 -0
- painapple_code/shadow_git.py +1296 -0
- painapple_code/shadow_git_frontmatter.py +314 -0
- painapple_code/shadow_git_sections.py +520 -0
- painapple_code/shadow_git_summary.py +462 -0
- painapple_code/shadow_parser.py +560 -0
- painapple_code/static/css/00-variables.css +276 -0
- painapple_code/static/css/01-base.css +245 -0
- painapple_code/static/css/02-tooltips.css +36 -0
- painapple_code/static/css/09-left-rail.css +448 -0
- painapple_code/static/css/10-header.css +777 -0
- painapple_code/static/css/11-connection-bar.css +120 -0
- painapple_code/static/css/20-messages.css +750 -0
- painapple_code/static/css/21-tool-blocks.css +2340 -0
- painapple_code/static/css/22-edit-diff.css +619 -0
- painapple_code/static/css/23-thinking.css +1584 -0
- painapple_code/static/css/24-background-tasks.css +359 -0
- painapple_code/static/css/25-context-block.css +252 -0
- painapple_code/static/css/26-session-setup.css +144 -0
- painapple_code/static/css/30-markdown.css +854 -0
- painapple_code/static/css/40-input.css +2418 -0
- painapple_code/static/css/41-autocomplete.css +127 -0
- painapple_code/static/css/42-activity-strip.css +97 -0
- painapple_code/static/css/43-file-autocomplete.css +121 -0
- painapple_code/static/css/44-snippets-autocomplete.css +224 -0
- painapple_code/static/css/45-selection-bar.css +433 -0
- painapple_code/static/css/46-keyboard-bar.css +119 -0
- painapple_code/static/css/47-keyboard-longpress.css +116 -0
- painapple_code/static/css/47-token-profile.css +106 -0
- painapple_code/static/css/50-welcome.css +3095 -0
- painapple_code/static/css/51-modals.css +301 -0
- painapple_code/static/css/52-utilities.css +77 -0
- painapple_code/static/css/53-image-upload.css +456 -0
- painapple_code/static/css/54-todo-list.css +102 -0
- painapple_code/static/css/55-file-upload.css +113 -0
- painapple_code/static/css/56-chat-navigator.css +218 -0
- painapple_code/static/css/56-permission-card.css +332 -0
- painapple_code/static/css/56-plan-approval.css +204 -0
- painapple_code/static/css/56-question-form.css +602 -0
- painapple_code/static/css/57-swipe-indicator.css +95 -0
- painapple_code/static/css/58-session-families.css +627 -0
- painapple_code/static/css/59-about.css +196 -0
- painapple_code/static/css/61-log-explorer.css +723 -0
- painapple_code/static/css/62-json-tree.css +489 -0
- painapple_code/static/css/62-skills-widget.css +650 -0
- painapple_code/static/css/63-commands-widget.css +30 -0
- painapple_code/static/css/63-plugins-widget.css +174 -0
- painapple_code/static/css/63-terminal.css +516 -0
- painapple_code/static/css/64-search-files-widget.css +448 -0
- painapple_code/static/css/65-snippets-widget.css +426 -0
- painapple_code/static/css/66-active-sessions.css +979 -0
- painapple_code/static/css/66-agents-widget.css +265 -0
- painapple_code/static/css/66-file-explorer-widget.css +855 -0
- painapple_code/static/css/66-git-panel.css +791 -0
- painapple_code/static/css/66-history-explorer.css +1496 -0
- painapple_code/static/css/67-diff-viewer.css +602 -0
- painapple_code/static/css/67-editor-view.css +748 -0
- painapple_code/static/css/67-file-preview-widget.css +1873 -0
- painapple_code/static/css/68-browser-widget.css +337 -0
- painapple_code/static/css/68-compare-wizard.css +355 -0
- painapple_code/static/css/68-csv-preview.css +106 -0
- painapple_code/static/css/68-diff-preview.css +103 -0
- painapple_code/static/css/68-helpers-install-widget.css +491 -0
- painapple_code/static/css/68-image-annotate.css +346 -0
- painapple_code/static/css/68-terminal-view.css +98 -0
- painapple_code/static/css/68-uploads-widget.css +229 -0
- painapple_code/static/css/69-cost-analytics.css +537 -0
- painapple_code/static/css/70-config-panel.css +3736 -0
- painapple_code/static/css/70-excalidraw.css +136 -0
- painapple_code/static/css/70-prompt-explorer.css +890 -0
- painapple_code/static/css/71-chart.css +111 -0
- painapple_code/static/css/72-discussion-sidebar.css +678 -0
- painapple_code/static/css/74-debug-logs.css +656 -0
- painapple_code/static/css/80-open-dialog.css +146 -0
- painapple_code/static/css/80-quick-switcher.css +383 -0
- painapple_code/static/css/80-widget-system.css +1763 -0
- painapple_code/static/css/81-quick-actions.css +1020 -0
- painapple_code/static/css/82-context-popover.css +1233 -0
- painapple_code/static/css/83-context-menu.css +339 -0
- painapple_code/static/css/85-lazy-loading.css +102 -0
- painapple_code/static/css/86-zen-mode.css +708 -0
- painapple_code/static/css/87-grid-switcher.css +346 -0
- painapple_code/static/css/login.css +198 -0
- painapple_code/static/dist/app.bundle.js +7177 -0
- painapple_code/static/feature-triage.html +446 -0
- painapple_code/static/icons/apple-touch-icon.png +0 -0
- painapple_code/static/icons/favicon-32.png +0 -0
- painapple_code/static/icons/favicon.ico +0 -0
- painapple_code/static/icons/icon-128.png +0 -0
- painapple_code/static/icons/icon-144.png +0 -0
- painapple_code/static/icons/icon-152.png +0 -0
- painapple_code/static/icons/icon-180.png +0 -0
- painapple_code/static/icons/icon-192.png +0 -0
- painapple_code/static/icons/icon-384.png +0 -0
- painapple_code/static/icons/icon-512.png +0 -0
- painapple_code/static/icons/icon-72.png +0 -0
- painapple_code/static/icons/icon-96.png +0 -0
- painapple_code/static/js/action-delegate.js +187 -0
- painapple_code/static/js/activity-strip.js +193 -0
- painapple_code/static/js/app/context-menus.js +619 -0
- painapple_code/static/js/app/file-preview.js +163 -0
- painapple_code/static/js/app/init-ui.js +574 -0
- painapple_code/static/js/app/input-events.js +561 -0
- painapple_code/static/js/app/input-resize.js +313 -0
- painapple_code/static/js/app/message-actions.js +419 -0
- painapple_code/static/js/app/panels.js +210 -0
- painapple_code/static/js/app/rendering-delegators.js +246 -0
- painapple_code/static/js/app/server-loading.js +365 -0
- painapple_code/static/js/app/session-ops.js +287 -0
- painapple_code/static/js/app/session-switch.js +443 -0
- painapple_code/static/js/app/settings-helpers.js +280 -0
- painapple_code/static/js/app/startup-welcome.js +290 -0
- painapple_code/static/js/app-context.js +157 -0
- painapple_code/static/js/app.js +2134 -0
- painapple_code/static/js/auth-fetch.js +46 -0
- painapple_code/static/js/background-tasks.js +274 -0
- painapple_code/static/js/boot.js +73 -0
- painapple_code/static/js/caret-position.js +171 -0
- painapple_code/static/js/chat-navigator.js +710 -0
- painapple_code/static/js/chat-search.js +323 -0
- painapple_code/static/js/command-executor.js +176 -0
- painapple_code/static/js/command-store.js +497 -0
- painapple_code/static/js/components.js +1414 -0
- painapple_code/static/js/config.js +218 -0
- painapple_code/static/js/context-menu.js +740 -0
- painapple_code/static/js/controllers/chat-controller.js +4252 -0
- painapple_code/static/js/controllers/dialog-controller.js +217 -0
- painapple_code/static/js/controllers/tab-controller.js +1828 -0
- painapple_code/static/js/controllers/thinking-controller.js +915 -0
- painapple_code/static/js/diff-utils.js +486 -0
- painapple_code/static/js/drafts-pill.js +95 -0
- painapple_code/static/js/editor-view.js +815 -0
- painapple_code/static/js/effort-settings.js +430 -0
- painapple_code/static/js/feature-triage.js +811 -0
- painapple_code/static/js/file-autocomplete.js +516 -0
- painapple_code/static/js/file-tabs.js +123 -0
- painapple_code/static/js/gestures.js +448 -0
- painapple_code/static/js/grid-switcher.js +334 -0
- painapple_code/static/js/input-handler.js +1472 -0
- painapple_code/static/js/keyboard-bar.js +495 -0
- painapple_code/static/js/layout-switcher.js +109 -0
- painapple_code/static/js/linkify-utils.js +200 -0
- painapple_code/static/js/login.js +141 -0
- painapple_code/static/js/open-dialog.js +879 -0
- painapple_code/static/js/orphan-terminals.js +373 -0
- painapple_code/static/js/path-utils.js +179 -0
- painapple_code/static/js/perf-marks.js +102 -0
- painapple_code/static/js/permission-settings.js +475 -0
- painapple_code/static/js/pointer-input.js +59 -0
- painapple_code/static/js/preview/json-tree-menu.js +226 -0
- painapple_code/static/js/preview/json-tree-toolbar.js +291 -0
- painapple_code/static/js/preview/json-tree.js +457 -0
- painapple_code/static/js/preview/preview-breadcrumb.js +387 -0
- painapple_code/static/js/preview/preview-edit.js +367 -0
- painapple_code/static/js/preview/preview-events.js +361 -0
- painapple_code/static/js/preview/preview-history.js +567 -0
- painapple_code/static/js/preview/preview-inline-edit.js +869 -0
- painapple_code/static/js/preview/preview-poll.js +108 -0
- painapple_code/static/js/preview/preview-render.js +572 -0
- painapple_code/static/js/preview/preview-search.js +421 -0
- painapple_code/static/js/preview/preview-state.js +256 -0
- painapple_code/static/js/preview/preview-utils.js +251 -0
- painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
- painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
- painapple_code/static/js/preview-plugins/diff-plugin.js +258 -0
- painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
- painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
- painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
- painapple_code/static/js/preview-plugins/index.js +34 -0
- painapple_code/static/js/preview-plugins/json-plugin.js +99 -0
- painapple_code/static/js/preview-plugins/jsonl-plugin.js +152 -0
- painapple_code/static/js/preview-plugins/markdown-plugin.js +266 -0
- painapple_code/static/js/preview-plugins/panzoom-plugin.js +63 -0
- painapple_code/static/js/preview-plugins/plugin-helpers.js +282 -0
- painapple_code/static/js/project-color-picker.js +110 -0
- painapple_code/static/js/project-colors.js +163 -0
- painapple_code/static/js/prompt-favorites.js +187 -0
- painapple_code/static/js/quick-actions-menu.js +1736 -0
- painapple_code/static/js/quick-actions-registry.js +1239 -0
- painapple_code/static/js/quick-switcher/controller.js +294 -0
- painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
- painapple_code/static/js/quick-switcher/index.js +30 -0
- painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
- painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
- painapple_code/static/js/quick-switcher/providers/file-provider.js +291 -0
- painapple_code/static/js/quick-switcher/providers/panel-provider.js +132 -0
- painapple_code/static/js/quick-switcher/providers/project-provider.js +361 -0
- painapple_code/static/js/quick-switcher/providers/read-files-provider.js +104 -0
- painapple_code/static/js/quick-switcher/providers/skills-provider.js +129 -0
- painapple_code/static/js/quick-switcher/registry.js +69 -0
- painapple_code/static/js/quick-switcher/ui/item.js +54 -0
- painapple_code/static/js/quick-switcher/ui/picker.js +464 -0
- painapple_code/static/js/recent-opens.js +40 -0
- painapple_code/static/js/scroll-manager.js +530 -0
- painapple_code/static/js/scroll-state-machine.js +359 -0
- painapple_code/static/js/selection/action-bar.js +793 -0
- painapple_code/static/js/selection/index.js +15 -0
- painapple_code/static/js/selection/selection-handler.js +877 -0
- painapple_code/static/js/selection/state.js +181 -0
- painapple_code/static/js/session/agent-progress.js +202 -0
- painapple_code/static/js/session/handle-agent-message.js +481 -0
- painapple_code/static/js/session/handle-message.js +550 -0
- painapple_code/static/js/session/interactive.js +587 -0
- painapple_code/static/js/session/message-store.js +271 -0
- painapple_code/static/js/session/messages.js +253 -0
- painapple_code/static/js/session/persistence.js +179 -0
- painapple_code/static/js/session/restore.js +136 -0
- painapple_code/static/js/session/sync.js +333 -0
- painapple_code/static/js/session-container-pool.js +371 -0
- painapple_code/static/js/session-setup-panel.js +274 -0
- painapple_code/static/js/session.js +1047 -0
- painapple_code/static/js/shortcut-hints.js +272 -0
- painapple_code/static/js/shortcuts.js +1146 -0
- painapple_code/static/js/skills-autocomplete.js +262 -0
- painapple_code/static/js/snippets-autocomplete.js +738 -0
- painapple_code/static/js/stash-refs-view.js +83 -0
- painapple_code/static/js/stash-ui.js +768 -0
- painapple_code/static/js/stash.js +785 -0
- painapple_code/static/js/status-bar.js +1184 -0
- painapple_code/static/js/token-profile.js +274 -0
- painapple_code/static/js/tool-renderer-blocks.js +1626 -0
- painapple_code/static/js/tool-renderer-thinking.js +813 -0
- painapple_code/static/js/tool-renderer.js +1500 -0
- painapple_code/static/js/tooltips.js +197 -0
- painapple_code/static/js/upload-manager.js +1019 -0
- painapple_code/static/js/utils.js +521 -0
- painapple_code/static/js/welcome/api.js +363 -0
- painapple_code/static/js/welcome/cards.js +626 -0
- painapple_code/static/js/welcome/context-menu.js +585 -0
- painapple_code/static/js/welcome/families.js +801 -0
- painapple_code/static/js/welcome/preview.js +234 -0
- painapple_code/static/js/welcome/state.js +129 -0
- painapple_code/static/js/welcome.js +1874 -0
- painapple_code/static/js/widget-system/base-widget.js +717 -0
- painapple_code/static/js/widget-system/device-manager.js +178 -0
- painapple_code/static/js/widget-system/event-bus.js +152 -0
- painapple_code/static/js/widget-system/icons.js +120 -0
- painapple_code/static/js/widget-system/index.js +49 -0
- painapple_code/static/js/widget-system/init.js +54 -0
- painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
- painapple_code/static/js/widget-system/types/floating.js +915 -0
- painapple_code/static/js/widget-system/types/index.js +40 -0
- painapple_code/static/js/widget-system/types/modal.js +147 -0
- painapple_code/static/js/widget-system/types/sidebar.js +158 -0
- painapple_code/static/js/widget-system/types/tab.js +146 -0
- painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
- painapple_code/static/js/widget-system/widget-manager.js +1187 -0
- painapple_code/static/js/widgets/about-widget.js +260 -0
- painapple_code/static/js/widgets/active-sessions-widget.js +760 -0
- painapple_code/static/js/widgets/agents-widget.js +897 -0
- painapple_code/static/js/widgets/browser-widget.js +579 -0
- painapple_code/static/js/widgets/commands-widget.js +694 -0
- painapple_code/static/js/widgets/config/commit-sections.js +449 -0
- painapple_code/static/js/widgets/config/dir-autocomplete.js +224 -0
- painapple_code/static/js/widgets/config/gestures.js +161 -0
- painapple_code/static/js/widgets/config/models-tab.js +988 -0
- painapple_code/static/js/widgets/config/quick-actions-tab.js +882 -0
- painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
- painapple_code/static/js/widgets/config/state.js +517 -0
- painapple_code/static/js/widgets/config/system-controls.js +90 -0
- painapple_code/static/js/widgets/config-widget.js +1535 -0
- painapple_code/static/js/widgets/cost-analytics-widget.js +621 -0
- painapple_code/static/js/widgets/debug-widget.js +693 -0
- painapple_code/static/js/widgets/diff-viewer-widget.js +1420 -0
- painapple_code/static/js/widgets/discussion-widget.js +1107 -0
- painapple_code/static/js/widgets/file-explorer-widget.js +2166 -0
- painapple_code/static/js/widgets/file-preview-widget.js +861 -0
- painapple_code/static/js/widgets/git-widget.js +794 -0
- painapple_code/static/js/widgets/helpers-install-widget.js +720 -0
- painapple_code/static/js/widgets/history-explorer-widget.js +1804 -0
- painapple_code/static/js/widgets/image-annotate-widget.js +951 -0
- painapple_code/static/js/widgets/image-preview-widget.js +692 -0
- painapple_code/static/js/widgets/index.js +176 -0
- painapple_code/static/js/widgets/log-explorer-widget.js +1057 -0
- painapple_code/static/js/widgets/plugins-widget.js +305 -0
- painapple_code/static/js/widgets/prompt-explorer-widget.js +1119 -0
- painapple_code/static/js/widgets/search-files-widget.js +529 -0
- painapple_code/static/js/widgets/skills-widget.js +843 -0
- painapple_code/static/js/widgets/snippets-widget.js +479 -0
- painapple_code/static/js/widgets/sub-agents-widget.js +333 -0
- painapple_code/static/js/widgets/tasks-widget.js +379 -0
- painapple_code/static/js/widgets/terminal/connection.js +283 -0
- painapple_code/static/js/widgets/terminal/gestures.js +357 -0
- painapple_code/static/js/widgets/terminal/init.js +642 -0
- painapple_code/static/js/widgets/terminal/link-providers.js +532 -0
- painapple_code/static/js/widgets/terminal/osc52.js +213 -0
- painapple_code/static/js/widgets/terminal/render.js +159 -0
- painapple_code/static/js/widgets/terminal/size.js +54 -0
- painapple_code/static/js/widgets/terminal/state.js +211 -0
- painapple_code/static/js/widgets/terminal/wrap-utils.js +101 -0
- painapple_code/static/js/widgets/terminal-widget.js +793 -0
- painapple_code/static/js/widgets/uploads-widget.js +461 -0
- painapple_code/static/js/widgets/zen-widget.js +1001 -0
- painapple_code/static/login.html +51 -0
- painapple_code/static/manifest.json +73 -0
- painapple_code/static/sw.js +293 -0
- painapple_code/static/vendor/LICENSES.txt +769 -0
- painapple_code/static/vendor/README.md +103 -0
- painapple_code/static/vendor/codemirror.js +35 -0
- painapple_code/static/vendor/codemirror.js.LEGAL.txt +304 -0
- painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
- painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
- painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
- painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
- painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
- painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
- painapple_code/static/vendor/highlight.min.js +1213 -0
- painapple_code/static/vendor/marked.min.js +6 -0
- painapple_code/static/vendor/purify.min.js +3 -0
- painapple_code/static/vendor/xterm-addon-fit.js +2 -0
- painapple_code/static/vendor/xterm.css +209 -0
- painapple_code/static/vendor/xterm.js +2 -0
- painapple_code/static/web-client.html +635 -0
- painapple_code/subprocess_registry.py +535 -0
- painapple_code/tls_cert.py +79 -0
- painapple_code/tools/agents/shadow-git-helper.md +400 -0
- painapple_code/tools/excalidraw-to-svg.js +143 -0
- painapple_code/tools/install-helpers.sh +148 -0
- painapple_code/tools/shadow-git +320 -0
- painapple_code/tools/shadow-query +120 -0
- painapple_code/tools/vegalite-to-svg.js +79 -0
- painapple_code/turn_query.py +766 -0
- painapple_code/turn_tracker.py +221 -0
- painapple_code/utils/__init__.py +0 -0
- painapple_code/utils/agent_cli.py +306 -0
- painapple_code/utils/chart.py +103 -0
- painapple_code/utils/excalidraw.py +107 -0
- painapple_code/utils/file_lock.py +162 -0
- painapple_code/utils/file_paths.py +648 -0
- painapple_code/utils/generate_icons.py +251 -0
- painapple_code/utils/proc.py +205 -0
- painapple_code/utils/pty_backend.py +59 -0
- painapple_code/utils/pty_posix.py +160 -0
- painapple_code/utils/pty_windows.py +245 -0
- painapple_code/utils/token_profiles.py +91 -0
- painapple_code/viewer_templates.py +657 -0
- painapple_code/welcome_search.py +879 -0
- painapple_code-1.0.0.dist-info/METADATA +292 -0
- painapple_code-1.0.0.dist-info/RECORD +436 -0
- painapple_code-1.0.0.dist-info/WHEEL +5 -0
- painapple_code-1.0.0.dist-info/entry_points.txt +2 -0
- painapple_code-1.0.0.dist-info/licenses/LICENSE +661 -0
- painapple_code-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +261 -0
- painapple_code-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message-action mixin — clipboard copy (copyCode / copyToolOutput / copyMessage),
|
|
3
|
+
* toggleMessageFavorite, the AskUserQuestion answer flow (submitQuestionAnswers /
|
|
4
|
+
* ignoreQuestion / updateQuestionMessage), and plan approve/reject/preview.
|
|
5
|
+
* Extracted from app.js; applied to App.prototype via Object.assign. Uses `this`
|
|
6
|
+
* (App instance) plus the imports below.
|
|
7
|
+
*/
|
|
8
|
+
import { $ } from '../utils.js';
|
|
9
|
+
import { FilePreviewWidget } from '../widget-system/init.js';
|
|
10
|
+
|
|
11
|
+
export const messageActionMethods = {
|
|
12
|
+
copyCode(codeId) {
|
|
13
|
+
const codeEl = $(`#${codeId}`);
|
|
14
|
+
if (codeEl) {
|
|
15
|
+
navigator.clipboard.writeText(codeEl.textContent);
|
|
16
|
+
const btn = $(`[data-code-id="${codeId}"]`);
|
|
17
|
+
if (btn) {
|
|
18
|
+
btn.classList.add('copied');
|
|
19
|
+
btn.innerHTML = `
|
|
20
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
21
|
+
<path d="M20 6L9 17l-5-5"/>
|
|
22
|
+
</svg>
|
|
23
|
+
Copied!
|
|
24
|
+
`;
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
btn.classList.remove('copied');
|
|
27
|
+
btn.innerHTML = `
|
|
28
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
29
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
|
30
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
31
|
+
</svg>
|
|
32
|
+
Copy
|
|
33
|
+
`;
|
|
34
|
+
}, 2000);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
copyToolOutput(msgId) {
|
|
40
|
+
const msg = this.activeSession?.messages.find(m => m.id === msgId);
|
|
41
|
+
if (msg && (msg.toolOutput || msg.toolError)) {
|
|
42
|
+
navigator.clipboard.writeText(msg.toolOutput || msg.toolError);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
copyMessage(msgId) {
|
|
47
|
+
const msg = this.activeSession?.messages.find(m => m.id === msgId);
|
|
48
|
+
if (msg && msg.content) {
|
|
49
|
+
navigator.clipboard.writeText(msg.content);
|
|
50
|
+
const btn = $(`[data-msg-id="${msgId}"].message-copy-btn`);
|
|
51
|
+
if (btn) {
|
|
52
|
+
btn.classList.add('copied');
|
|
53
|
+
btn.innerHTML = `
|
|
54
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
55
|
+
<path d="M20 6L9 17l-5-5"/>
|
|
56
|
+
</svg>
|
|
57
|
+
<span class="copy-label">Copied!</span>
|
|
58
|
+
`;
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
btn.classList.remove('copied');
|
|
61
|
+
btn.innerHTML = `
|
|
62
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
63
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
|
64
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
65
|
+
</svg>
|
|
66
|
+
<span class="copy-label">Copy</span>
|
|
67
|
+
`;
|
|
68
|
+
}, 2000);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Toggle favorite state for a user message
|
|
75
|
+
* Called from the favorite button in message header
|
|
76
|
+
* @param {HTMLElement} btn - The button element
|
|
77
|
+
*/
|
|
78
|
+
async toggleMessageFavorite(btn) {
|
|
79
|
+
const msgId = btn.dataset.msgId;
|
|
80
|
+
const promptId = btn.dataset.promptId;
|
|
81
|
+
|
|
82
|
+
if (!promptId) {
|
|
83
|
+
console.warn('[App] Cannot favorite message without promptId');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Find the message in active session (use window.app to avoid this binding issues)
|
|
88
|
+
const app = window.app;
|
|
89
|
+
const msg = app?.activeSession?.messages?.find(m => m.id === msgId);
|
|
90
|
+
|
|
91
|
+
// Optimistic UI update
|
|
92
|
+
const wasActive = btn.classList.contains('active');
|
|
93
|
+
btn.classList.toggle('active');
|
|
94
|
+
btn.setAttribute('data-tooltip', wasActive ? 'Add to favorites' : 'Remove from favorites');
|
|
95
|
+
|
|
96
|
+
// Import and call the API
|
|
97
|
+
try {
|
|
98
|
+
const { toggleFavorite } = await import('../prompt-favorites.js');
|
|
99
|
+
// Get content preview from message if found, otherwise from button's parent message
|
|
100
|
+
const contentPreview = msg?.content?.substring(0, 100) || '';
|
|
101
|
+
const newState = await toggleFavorite(promptId, contentPreview);
|
|
102
|
+
|
|
103
|
+
// Update session message state if found
|
|
104
|
+
if (msg) {
|
|
105
|
+
msg.isFavorite = newState;
|
|
106
|
+
app?.sessionManager?.saveSessions();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Revert UI if API state doesn't match expected
|
|
110
|
+
if (newState === wasActive) {
|
|
111
|
+
btn.classList.toggle('active');
|
|
112
|
+
btn.setAttribute('data-tooltip', newState ? 'Remove from favorites' : 'Add to favorites');
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.error('[App] Failed to toggle favorite:', err);
|
|
116
|
+
// Revert UI on error
|
|
117
|
+
btn.classList.toggle('active');
|
|
118
|
+
btn.setAttribute('data-tooltip', wasActive ? 'Remove from favorites' : 'Add to favorites');
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════
|
|
123
|
+
// ASKUSERQUESTION HANDLING
|
|
124
|
+
// ═══════════════════════════════════════════════════════════════
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Submit answers for AskUserQuestion tool (supports both single and grouped wizard mode)
|
|
128
|
+
* Called from the submit button in the question form
|
|
129
|
+
* @param {string} msgId - The message ID containing question(s)
|
|
130
|
+
*/
|
|
131
|
+
submitQuestionAnswers(msgId) {
|
|
132
|
+
const session = this.activeSession;
|
|
133
|
+
if (!session) {
|
|
134
|
+
console.error('No active session');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Find the question message
|
|
139
|
+
const questionMsg = session.messages.find(m => m.id === msgId && m.role === 'question');
|
|
140
|
+
if (!questionMsg) {
|
|
141
|
+
console.error('Question message not found:', msgId);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const form = document.querySelector(`.question-form[data-msg-id="${msgId}"]`);
|
|
146
|
+
if (!form) {
|
|
147
|
+
console.error('Question form not found:', msgId);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Get entries (support both old single format and new grouped format)
|
|
152
|
+
const entries = questionMsg.entries || [{
|
|
153
|
+
toolId: questionMsg.toolId,
|
|
154
|
+
questions: questionMsg.questions || [],
|
|
155
|
+
answers: {}
|
|
156
|
+
}];
|
|
157
|
+
|
|
158
|
+
// Collect answers from all entries
|
|
159
|
+
let hasAnyAnswer = false;
|
|
160
|
+
|
|
161
|
+
form.querySelectorAll('.question-entry').forEach((entryEl, entryIdx) => {
|
|
162
|
+
const entry = entries[entryIdx];
|
|
163
|
+
if (!entry) return;
|
|
164
|
+
|
|
165
|
+
entryEl.querySelectorAll('.question-item').forEach(item => {
|
|
166
|
+
const header = item.dataset.header;
|
|
167
|
+
const isMulti = item.dataset.multi === 'true';
|
|
168
|
+
const selectedOptions = item.querySelectorAll('.question-option.selected');
|
|
169
|
+
|
|
170
|
+
if (isMulti) {
|
|
171
|
+
const values = Array.from(selectedOptions)
|
|
172
|
+
.map(btn => {
|
|
173
|
+
if (btn.dataset.value === '__other__') {
|
|
174
|
+
const input = item.querySelector('.question-other-input');
|
|
175
|
+
return input?.value?.trim() || '';
|
|
176
|
+
}
|
|
177
|
+
return btn.dataset.value;
|
|
178
|
+
})
|
|
179
|
+
.filter(v => v);
|
|
180
|
+
if (values.length > 0) {
|
|
181
|
+
entry.answers[header] = values.join(', ');
|
|
182
|
+
hasAnyAnswer = true;
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
const selected = selectedOptions[0];
|
|
186
|
+
if (selected) {
|
|
187
|
+
if (selected.dataset.value === '__other__') {
|
|
188
|
+
const input = item.querySelector('.question-other-input');
|
|
189
|
+
entry.answers[header] = input?.value?.trim() || 'Other';
|
|
190
|
+
} else {
|
|
191
|
+
entry.answers[header] = selected.dataset.value;
|
|
192
|
+
}
|
|
193
|
+
hasAnyAnswer = true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Free-text comment (shared across the card, independent of the options).
|
|
200
|
+
const commentEl = form.querySelector('.question-comment');
|
|
201
|
+
const comment = commentEl?.value?.trim() || '';
|
|
202
|
+
|
|
203
|
+
// Need at least one selected answer OR a comment — a wholly empty submit
|
|
204
|
+
// shakes the form.
|
|
205
|
+
if (!hasAnyAnswer && !comment) {
|
|
206
|
+
form.classList.add('shake');
|
|
207
|
+
setTimeout(() => form.classList.remove('shake'), 500);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Persist the comment so it re-renders on the answered card / restore.
|
|
212
|
+
questionMsg.comment = comment;
|
|
213
|
+
|
|
214
|
+
// Send answers for each entry (each has its own tool_id)
|
|
215
|
+
session.sendToolAnswers(entries, questionMsg, comment);
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Dismiss an AskUserQuestion without sending anything to Claude.
|
|
220
|
+
* Marks the message as answered+ignored locally so the banner clears
|
|
221
|
+
* and the form collapses, but no tool_answer is sent over the WS.
|
|
222
|
+
*/
|
|
223
|
+
ignoreQuestion(msgId) {
|
|
224
|
+
const session = this.activeSession;
|
|
225
|
+
if (!session) return;
|
|
226
|
+
|
|
227
|
+
const questionMsg = session.messages.find(m => m.id === msgId && m.role === 'question');
|
|
228
|
+
if (!questionMsg || questionMsg.answered) return;
|
|
229
|
+
|
|
230
|
+
questionMsg.answered = true;
|
|
231
|
+
questionMsg.ignored = true;
|
|
232
|
+
|
|
233
|
+
this.updateQuestionMessage(questionMsg);
|
|
234
|
+
|
|
235
|
+
session.pendingQuestionId = null;
|
|
236
|
+
session.updateTab();
|
|
237
|
+
this.updateQuestionIndicator();
|
|
238
|
+
this.sessionManager.saveSessions();
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Re-open an already-answered AskUserQuestion card so the user can change
|
|
243
|
+
* their choice and resend it to Claude. Sets a transient `_editing` flag and
|
|
244
|
+
* re-renders — the form comes back interactive with the previous answers
|
|
245
|
+
* pre-selected (see _renderQuestionForm / findAnswerRecord). No WS traffic
|
|
246
|
+
* until the user actually submits.
|
|
247
|
+
*/
|
|
248
|
+
editQuestionAnswer(msgId) {
|
|
249
|
+
const session = this.activeSession;
|
|
250
|
+
if (!session) return;
|
|
251
|
+
|
|
252
|
+
const questionMsg = session.messages.find(m => m.id === msgId && m.role === 'question');
|
|
253
|
+
if (!questionMsg || !questionMsg.answered) return;
|
|
254
|
+
// An ignored/skipped card was never actually answered — nothing to edit.
|
|
255
|
+
if (questionMsg.ignored || questionMsg.skipped) return;
|
|
256
|
+
|
|
257
|
+
questionMsg._editing = true;
|
|
258
|
+
this.updateQuestionMessage(questionMsg);
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
/** Abandon an in-progress answer edit and restore the answered view. */
|
|
262
|
+
cancelEditQuestion(msgId) {
|
|
263
|
+
const session = this.activeSession;
|
|
264
|
+
if (!session) return;
|
|
265
|
+
|
|
266
|
+
const questionMsg = session.messages.find(m => m.id === msgId && m.role === 'question');
|
|
267
|
+
if (!questionMsg) return;
|
|
268
|
+
|
|
269
|
+
questionMsg._editing = false;
|
|
270
|
+
this.updateQuestionMessage(questionMsg);
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
/** @delegate ChatController */
|
|
274
|
+
updateQuestionMessage(msg) {
|
|
275
|
+
this.chatCtrl.updateQuestionMessage(msg);
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Answer an interactive permission card (claude-sdk can_use_tool ask).
|
|
280
|
+
* Reads the optional deny-guidance input and relays the decision over the
|
|
281
|
+
* session WebSocket; the provider process resumes as soon as it lands.
|
|
282
|
+
*/
|
|
283
|
+
respondPermission(msgId, behavior, suggestionIndex) {
|
|
284
|
+
const session = this.activeSession;
|
|
285
|
+
if (!session) return;
|
|
286
|
+
const feedback = behavior === 'deny'
|
|
287
|
+
? (document.getElementById(`perm-feedback-${msgId}`)?.value || '').trim()
|
|
288
|
+
: '';
|
|
289
|
+
session.sendPermissionResponse(msgId, behavior, feedback, suggestionIndex);
|
|
290
|
+
this.updateQuestionIndicator();
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The newest unanswered interactive permission card in the active session,
|
|
295
|
+
* or null. Backs the Enter=Allow / Esc=Deny keyboard shortcuts so the user
|
|
296
|
+
* can clear a pending ask without reaching for the mouse.
|
|
297
|
+
*/
|
|
298
|
+
pendingPermissionCard() {
|
|
299
|
+
const session = this.activeSession;
|
|
300
|
+
if (!session) return null;
|
|
301
|
+
for (let i = session.messages.length - 1; i >= 0; i--) {
|
|
302
|
+
const m = session.messages[i];
|
|
303
|
+
if (m.role === 'permission' && !m.answered) return m;
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Allow the pending permission card via keyboard (Enter). No-op when there
|
|
310
|
+
* is no card waiting — the shortcut's `permissionPending` context already
|
|
311
|
+
* gates this, so the guard is just belt-and-suspenders.
|
|
312
|
+
*/
|
|
313
|
+
allowPendingPermission() {
|
|
314
|
+
const msg = this.pendingPermissionCard();
|
|
315
|
+
if (!msg) return;
|
|
316
|
+
this.respondPermission(msg.id, 'allow');
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Deny the pending permission card via keyboard (Esc). Any text typed into
|
|
321
|
+
* the deny-guidance field rides along as feedback. Returns true when a card
|
|
322
|
+
* was denied so handleEscape() can stop its priority chain.
|
|
323
|
+
*/
|
|
324
|
+
denyPendingPermission() {
|
|
325
|
+
const msg = this.pendingPermissionCard();
|
|
326
|
+
if (!msg) return false;
|
|
327
|
+
this.respondPermission(msg.id, 'deny');
|
|
328
|
+
return true;
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Approve plan from ExitPlanMode approval card
|
|
333
|
+
* Sends approval as a regular user message and resumes Claude
|
|
334
|
+
*/
|
|
335
|
+
approvePlan(msgId) {
|
|
336
|
+
const session = this.activeSession;
|
|
337
|
+
if (!session) return;
|
|
338
|
+
|
|
339
|
+
const msg = session.messages.find(m => m.id === msgId && m.role === 'plan_approval');
|
|
340
|
+
if (!msg || msg.answered) return;
|
|
341
|
+
|
|
342
|
+
msg.answered = true;
|
|
343
|
+
msg.decision = 'approve';
|
|
344
|
+
|
|
345
|
+
// Re-render the approval card
|
|
346
|
+
const el = document.getElementById(`msg-${msg.id}`);
|
|
347
|
+
if (el) {
|
|
348
|
+
const newEl = this.chatCtrl.createMessageElement(msg);
|
|
349
|
+
el.replaceWith(newEl);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Clear pending question state
|
|
353
|
+
session.pendingQuestionId = null;
|
|
354
|
+
session.updateTab();
|
|
355
|
+
this.updateQuestionIndicator();
|
|
356
|
+
this.sessionManager.saveSessions();
|
|
357
|
+
|
|
358
|
+
// Send approval as a regular user message
|
|
359
|
+
this.sendMessage('Approved. Proceed with the plan implementation.');
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Reject plan from ExitPlanMode approval card
|
|
364
|
+
* Re-enters plan mode and lets user type feedback
|
|
365
|
+
*/
|
|
366
|
+
rejectPlan(msgId) {
|
|
367
|
+
const session = this.activeSession;
|
|
368
|
+
if (!session) return;
|
|
369
|
+
|
|
370
|
+
const msg = session.messages.find(m => m.id === msgId && m.role === 'plan_approval');
|
|
371
|
+
if (!msg || msg.answered) return;
|
|
372
|
+
|
|
373
|
+
// Soft state — user is revising, but can still Approve later
|
|
374
|
+
msg.decision = 'revise';
|
|
375
|
+
|
|
376
|
+
// Re-render the approval card (shows revise status + Approve button)
|
|
377
|
+
const el = document.getElementById(`msg-${msg.id}`);
|
|
378
|
+
if (el) {
|
|
379
|
+
const newEl = this.chatCtrl.createMessageElement(msg);
|
|
380
|
+
el.replaceWith(newEl);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Re-enter plan mode on client
|
|
384
|
+
session.permissionMode = 'plan';
|
|
385
|
+
session.pendingQuestionId = null;
|
|
386
|
+
session.updateTab();
|
|
387
|
+
this.updateInputPlaceholder();
|
|
388
|
+
this.updateQuestionIndicator();
|
|
389
|
+
this.sessionManager.saveSessions();
|
|
390
|
+
|
|
391
|
+
// Re-enter plan mode on server (so restarts use --permission-mode plan)
|
|
392
|
+
if (session.ws?.readyState === WebSocket.OPEN) {
|
|
393
|
+
session.ws.send(JSON.stringify({
|
|
394
|
+
type: 'set_permission_mode',
|
|
395
|
+
mode: 'plan'
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Focus input so user can type feedback
|
|
400
|
+
this.els.messageInput?.focus();
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Preview the plan file for the current session.
|
|
405
|
+
* Plan file path is stored on the plan_approval message by handleExitPlanMode.
|
|
406
|
+
*/
|
|
407
|
+
async previewPlan() {
|
|
408
|
+
const session = this.activeSession;
|
|
409
|
+
if (!session) return;
|
|
410
|
+
|
|
411
|
+
// Find the latest plan_approval message with a planFile
|
|
412
|
+
const msg = [...session.messages].reverse().find(m => m.role === 'plan_approval' && m.planFile);
|
|
413
|
+
if (!msg?.planFile) return;
|
|
414
|
+
|
|
415
|
+
// Use the already-imported FilePreviewWidget (not dynamic import, which would
|
|
416
|
+
// load a separate module instance due to cache-bust query params)
|
|
417
|
+
FilePreviewWidget.preview(msg.planFile);
|
|
418
|
+
},
|
|
419
|
+
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel & widget toggle mixin — thin delegators that open/close/cycle the
|
|
3
|
+
* various floating widgets, panels, and switchers from keyboard shortcuts,
|
|
4
|
+
* rail buttons, and quick actions. Applied to App.prototype via Object.assign
|
|
5
|
+
* in app.js; every method uses `this` (App instance) and the imports below.
|
|
6
|
+
*/
|
|
7
|
+
import { WidgetManager, WidgetBus } from '../widget-system/init.js';
|
|
8
|
+
import { QuickSwitcher } from '../quick-switcher/index.js';
|
|
9
|
+
import { OpenDialog } from '../open-dialog.js';
|
|
10
|
+
import { QuickActionsRegistry } from '../quick-actions-registry.js';
|
|
11
|
+
import { QuickActionsMenu } from '../widgets/index.js';
|
|
12
|
+
import { bgTaskTracker } from '../background-tasks.js';
|
|
13
|
+
import { effortSettings } from '../effort-settings.js';
|
|
14
|
+
|
|
15
|
+
export const panelMethods = {
|
|
16
|
+
toggleFileExplorer() {
|
|
17
|
+
this.fileExplorer?.toggle();
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
toggleSearchFiles() {
|
|
21
|
+
WidgetManager.toggle('search-files');
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
toggleQuickSwitcher() {
|
|
25
|
+
QuickSwitcher.toggle();
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// Command palette — same picker as the quick switcher, opened straight
|
|
29
|
+
// into command mode via the '>' prefix (VS Code / Cursor / Zed idiom).
|
|
30
|
+
// Pressing again while open just hides (toggle semantics).
|
|
31
|
+
openCommandPalette() {
|
|
32
|
+
if (QuickSwitcher.isOpen()) QuickSwitcher.hide();
|
|
33
|
+
else QuickSwitcher.show('>');
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
toggleOpenDialog() {
|
|
37
|
+
OpenDialog.toggle();
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
toggleGridSwitcher() {
|
|
41
|
+
this.gridSwitcher?.toggle();
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Cycle through cards in the grid switcher. If the grid is closed, opens it.
|
|
46
|
+
* If already open, advances focus by `direction` (+1 forward, -1 backward).
|
|
47
|
+
* Wired to Alt+Tab (forward) and Alt+Shift+Tab (backward).
|
|
48
|
+
*/
|
|
49
|
+
cycleGridSwitcher(direction = 1) {
|
|
50
|
+
if (!this.gridSwitcher) return;
|
|
51
|
+
if (this.gridSwitcher.visible) {
|
|
52
|
+
this.gridSwitcher.advance(direction);
|
|
53
|
+
} else {
|
|
54
|
+
// Mac/Windows app-switcher feel: open with the next session
|
|
55
|
+
// pre-selected (skipping the currently-active one) and commit
|
|
56
|
+
// on Alt-release.
|
|
57
|
+
this.gridSwitcher.show({
|
|
58
|
+
commitOnAltRelease: true,
|
|
59
|
+
initialDirection: direction,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
toggleLogExplorer() {
|
|
65
|
+
WidgetManager.toggle('log-explorer');
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
toggleGitPanel() {
|
|
69
|
+
WidgetManager.toggle('git');
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
toggleActiveSessions() {
|
|
73
|
+
WidgetManager.toggle('active-sessions');
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
toggleCostAnalytics() {
|
|
77
|
+
WidgetManager.toggle('cost-analytics');
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
toggleDiscussion() {
|
|
81
|
+
WidgetManager.toggle('discussion');
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
toggleDebugConsole() {
|
|
85
|
+
WidgetManager.toggle('debug-logs');
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
toggleHistoryExplorer() {
|
|
89
|
+
WidgetManager.toggle('history-explorer');
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
togglePromptExplorer() {
|
|
93
|
+
WidgetManager.toggle('prompt-explorer');
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Bank the current input as a server-side draft (Ctrl+Shift+S).
|
|
97
|
+
// Retrieval lives in the Prompt Explorer's Drafts tab.
|
|
98
|
+
savePromptDraft() {
|
|
99
|
+
this.inputHandler?.saveAsDraft();
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Explicit reload so Cmd+R works in the iOS/PWA standalone wrapper,
|
|
103
|
+
// where there's no browser chrome to handle it natively. In a normal
|
|
104
|
+
// browser this is equivalent to the native reload; Cmd+Shift+R (hard
|
|
105
|
+
// reload) is a different key and still passes through.
|
|
106
|
+
reloadPage() {
|
|
107
|
+
window.location.reload();
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
toggleSkills() {
|
|
111
|
+
WidgetManager.toggle('skills');
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
toggleCommands() {
|
|
115
|
+
WidgetManager.toggle('commands');
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async togglePreviewInlineEdit() {
|
|
119
|
+
// Use module namespace (not destructuring) so state is the live binding
|
|
120
|
+
// — activateState(tabId) reassigns the module-level state variable
|
|
121
|
+
const mod = await import('../preview/preview-state.js');
|
|
122
|
+
|
|
123
|
+
// Find file-preview container — could be floating widget OR a tab.
|
|
124
|
+
// Prefer the ACTIVE instance's own container (state.container is set on
|
|
125
|
+
// every render): a bare document.querySelector takes the first
|
|
126
|
+
// .file-preview-widget in DOM order, which with a floating preview +
|
|
127
|
+
// hidden tab previews open could be a pane the user isn't looking at —
|
|
128
|
+
// the viewMode check below and the toggle would then disagree
|
|
129
|
+
// ("e does nothing" / toggles a hidden pane).
|
|
130
|
+
const stateContainer = mod.state.container?.isConnected ? mod.state.container : null;
|
|
131
|
+
const container = stateContainer?.querySelector('.file-preview-widget')
|
|
132
|
+
|| stateContainer
|
|
133
|
+
|| document.querySelector('.file-preview-widget');
|
|
134
|
+
if (!container) return;
|
|
135
|
+
|
|
136
|
+
if (mod.state.viewMode === 'code' && mod.isEditable()) {
|
|
137
|
+
// Code view → enter edit mode, capturing visible line for scroll restoration
|
|
138
|
+
const scrollContainer = container.querySelector('.preview-body');
|
|
139
|
+
if (scrollContainer) {
|
|
140
|
+
const scrollTop = scrollContainer.scrollTop;
|
|
141
|
+
const lineRows = scrollContainer.querySelectorAll('.preview-line');
|
|
142
|
+
for (const row of lineRows) {
|
|
143
|
+
const rowTop = row.getBoundingClientRect().top - scrollContainer.getBoundingClientRect().top + scrollTop;
|
|
144
|
+
if (rowTop + row.offsetHeight > scrollTop) {
|
|
145
|
+
mod.state.scrollToLine = parseInt(row.dataset.line, 10);
|
|
146
|
+
mod.state.scrollOptions = { flash: false, position: 'center' };
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const { switchToEditView } = await import('../preview/preview-edit.js');
|
|
152
|
+
switchToEditView();
|
|
153
|
+
} else if (mod.isEditMode()) {
|
|
154
|
+
// Edit mode → leave to code view (leaveEditView captures cursor line)
|
|
155
|
+
const { leaveEditView } = await import('../preview/preview-edit.js');
|
|
156
|
+
leaveEditView('code');
|
|
157
|
+
} else {
|
|
158
|
+
// Rendered/other view → toggle inline edit
|
|
159
|
+
const { toggleInlineEdit } = await import('../preview/preview-inline-edit.js');
|
|
160
|
+
toggleInlineEdit(container);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
toggleBackgroundTasks() {
|
|
165
|
+
WidgetManager.toggle('background-tasks');
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
toggleBrowser() {
|
|
169
|
+
WidgetManager.toggle('browser');
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
toggleZenMode() {
|
|
173
|
+
window.toggleZenMode?.();
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
openBackgroundTask(taskId) {
|
|
177
|
+
WidgetManager.open('background-tasks');
|
|
178
|
+
WidgetBus.emit('background-task:focus', { taskId });
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
_updateBgTasksBadge() {
|
|
182
|
+
const badge = document.getElementById('tasks-badge');
|
|
183
|
+
if (!badge) return;
|
|
184
|
+
const running = bgTaskTracker.runningCount();
|
|
185
|
+
badge.textContent = running > 0 ? running : '';
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Update the running agents header badge.
|
|
190
|
+
* Called from session._handleTaskProgress / _onAgentCompleted.
|
|
191
|
+
*/
|
|
192
|
+
updateAgentsBadge(count) {
|
|
193
|
+
const badge = document.getElementById('agents-badge');
|
|
194
|
+
if (!badge) return;
|
|
195
|
+
badge.textContent = count > 0 ? count : '';
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
toggleThinkingSettings() {
|
|
199
|
+
effortSettings.cycle();
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
cycleEffortOneShot() {
|
|
203
|
+
effortSettings.cycleOneShot();
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
toggleQuickActions() {
|
|
208
|
+
QuickActionsMenu.toggle();
|
|
209
|
+
},
|
|
210
|
+
};
|