Compare commits

...

11 Commits

Author SHA1 Message Date
Developer 346b32236a debug: synthesize wheel events for mobile terminal touch scrolling
Replace manual scrollLines/SGR injection with synthetic WheelEvent
instances dispatched into xterm.js's .xterm-viewport. Keep the debug
overlay so we can verify whether the gesture is recognized and whether
the wheel event reaches xterm.js.

Quality gates: typecheck, lint clean.
2026-06-13 21:04:50 +00:00
Developer 8f7f682a92 fix: merge mobile terminal scroll detection improvements 2026-06-13 20:42:52 +00:00
Developer 2ece7074f6 fix: improve mobile terminal alternate-screen detection and scrolling
- Revert touch-action: none on .xterm-viewport so xterm.js can fall back
  to its own viewport scrolling when the custom handler doesn't take over.
- Detect alternate screen via reference equality
  (term.buffer.active === term.buffer.alternate) instead of the
  string, which could report normal buffer incorrectly.
- Lower vertical-scroll activation threshold from 4px to 2px and only
  prevent default once a vertical gesture is recognized.
- In normal buffer use term.scrollLines() so xterm.js handles the buffer
  scroll consistently; in alternate screen continue sending SGR 1006
  mouse-wheel sequences to tmux/vim.

Quality gates: typecheck, lint clean, npm test -- --run 87 passed.
2026-06-13 20:42:52 +00:00
Developer c7ab2de25e fix: merge mobile terminal scrolling improvements 2026-06-13 20:30:52 +00:00
Developer c9b0259993 fix: route mobile terminal swipes to the terminal, not the page layout
- Add touch-action: none and overscroll-behavior: none to mobile terminal
  page, content, wrapper, container, and xterm viewport so the browser
  never treats swipes as page/address-bar scrolling or pull-to-refresh.
- Make .terminal-page.mobile fixed-position to prevent viewport layout
  scroll; keep the overlay toolbar as absolute with pointer-events only
  on interactive parts.
- Rework the mobile touch handler in terminal.tsx:
  * Detect normal vs alternate buffer via term.buffer.active.type instead
    of measuring the DOM viewport, which was unreliable in tmux/vim.
  * Accumulate swipe distance and emit SGR 1006 mouse-wheel sequences in
    steps, so tmux pane scrolling tracks the gesture correctly.
  * Prevent default as soon as the swipe is recognized so the page does
    not start a competing scroll gesture.

Quality gates: npm run typecheck, npm run lint clean, npm test -- --run 87 passed.
2026-06-13 20:30:52 +00:00
Developer 1f2c3dbe2a fix: merge workspace project_name population 2026-06-13 19:59:13 +00:00
Developer d247e44985 fix: include project_name in /workspaces list response
- The top-level GET /workspaces endpoint was returning project_name as
  an empty string because the GitRepository.project relationship was not
  eager-loaded.
- Select the full GitRepository entity and apply selectinload(project)
  so project_name is populated for every workspace row.

Backend quality gates: python3 -m pytest 313 passed, 34 skipped.
2026-06-13 19:59:13 +00:00
Developer d60dec9d62 fix: merge workspace project name visibility 2026-06-13 19:53:43 +00:00
Developer 4913cc7297 fix: make workspace project name visible on mobile and desktop cards
- Move the project name below the workspace title row so it reads as a
  distinct line with a project icon.
- Move the status badge into the title row next to the workspace name,
  preventing it from crowding the project label.
- Add .workspace-title-row flex styles and update .workspace-project-name
  to display inline-flex with a brand-colored project icon.

Quality gates: npm run typecheck, npm run lint clean,
npm test -- --run 87 passed.
2026-06-13 19:53:43 +00:00
Developer 86360661e9 fix: merge nav sessions middle 2026-06-13 19:44:47 +00:00
Developer 1d09193652 fix: put Sessions back in the middle of nav on desktop and mobile
- Reorder desktop sidebar so Sessions sits between spaces/tools groups:
  Home, Projects, Workspaces, Sessions, Tool Workshop, Config Profiles, Settings.
- Reorder mobile bottom nav so Sessions is the center item:
  Home, Spaces, Sessions, Tools, Settings.
- Workspace cards already display the owning project name; no extra change needed.

Quality gates: npm run typecheck, npm run lint clean,
npm test -- --run 87 passed.
2026-06-13 19:44:47 +00:00
38 changed files with 410 additions and 307 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ dir: .
Trust boundary: index routes, map orients, source decides. Trust boundary: index routes, map orients, source decides.
## role ## role
Infrastructure and deployment configuration for a self-hosted project management and git repository platform with OAuth2 authentication. Infrastructure and deployment configuration package for a self-hosted project management platform with OAuth2 authentication, providing containerized orchestration, environment templates, and development tooling.
## parent ## parent
- -
## children ## children
+2 -2
View File
@@ -18,7 +18,7 @@ index: ./.pi-map.index.md
Trust boundary: index routes, map orients, source decides. Trust boundary: index routes, map orients, source decides.
## role ## role
Infrastructure and deployment configuration for a self-hosted project management and git repository platform with OAuth2 authentication. Infrastructure and deployment configuration package for a self-hosted project management platform with OAuth2 authentication, providing containerized orchestration, environment templates, and development tooling.
## files ## files
- .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment - .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment
- .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git - .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git
@@ -31,7 +31,7 @@ Infrastructure and deployment configuration for a self-hosted project management
- progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases - progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases
- swap-pane | Empty file with no functionality - swap-pane | Empty file with no functionality
## arch ## arch
Containerized microservices architecture using Docker Compose with PostgreSQL/Redis persistence, Traefik reverse proxy for TLS termination, environment-driven configuration, and a decoupled frontend/API backend structure. Docker Compose-based microservices architecture with frontend/backend separation, PostgreSQL/Redis data layer, Traefik reverse proxy integration, and environment-driven configuration management following twelve-factor app principles.
## tags ## tags
docker, redis, git, application, postgresql, compose, traefik, project docker, redis, git, application, postgresql, compose, traefik, project
## symbols ## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps dir: apps
## role ## role
Contains the main executable application modules that serve as entry points for the project. Contains the main deployable application modules or entry points for the project.
## parent ## parent
index: ./.pi-map.index.md index: ./.pi-map.index.md
map: ./.pi-map.md map: ./.pi-map.md
+2 -2
View File
@@ -4,10 +4,10 @@ dir: apps
index: apps/.pi-map.index.md index: apps/.pi-map.index.md
## role ## role
Contains the main executable application modules that serve as entry points for the project. Contains the main deployable application modules or entry points for the project.
## files ## files
## arch ## arch
Modular application structure with separate deployable units, likely following microservices or layered architecture patterns with domain-driven design boundaries. Modular monolith or microservices architecture with separate application boundaries, each potentially having its own configuration, dependencies, and lifecycle.
## tags ## tags
- -
## symbols ## symbols
+1 -1
View File
@@ -14,7 +14,7 @@ Self-hosted FastAPI backend API that manages projects, git repositories, and dev
- uv.lock | Lock file for the uv Python package manager that pins exact dependency versions and their artifact hashes for reproducible installations | dep: uv, Python 3.11+, aiosqlite, alembic, annotated-doc, annotated-types, anyio, ast-serialize, asyncpg, and many other PyPI packages - uv.lock | Lock file for the uv Python package manager that pins exact dependency versions and their artifact hashes for reproducible installations | dep: uv, Python 3.11+, aiosqlite, alembic, annotated-doc, annotated-types, anyio, ast-serialize, asyncpg, and many other PyPI packages
- wait-for-db.sh | Wait for a PostgreSQL database to become available before executing a command, with configurable retry logic. | dep: nc (netcat), sh (POSIX shell), sleep - wait-for-db.sh | Wait for a PostgreSQL database to become available before executing a command, with configurable retry logic. | dep: nc (netcat), sh (POSIX shell), sleep
## arch ## arch
Async Python/FastAPI backend with PostgreSQL (asyncpg), Alembic migrations, multi-stage Docker deployment with Cloudflare tunneling, uv package management, and containerized orchestration with database dependency waiting. Async Python/FastAPI with PostgreSQL (Alembic migrations), multi-stage Docker deployment with Cloudflare tunneling, uv package management, and containerized service orchestration.
## tags ## tags
docker, alembic, python, database, fastapi, postgresql, asyncpg, uvicorn docker, alembic, python, database, fastapi, postgresql, asyncpg, uvicorn
## symbols ## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/api/src dir: apps/api/src
## role ## role
Core application package for the Headquarter API, providing configuration, database connectivity, logging infrastructure, and FastAPI application initialization. Core API application package that initializes and configures the Headquarter FastAPI backend with database, authentication, logging, and middleware infrastructure.
## parent ## parent
index: apps/api/.pi-map.index.md index: apps/api/.pi-map.index.md
map: apps/api/.pi-map.md map: apps/api/.pi-map.md
+2 -2
View File
@@ -4,7 +4,7 @@ dir: apps/api/src
index: apps/api/src/.pi-map.index.md index: apps/api/src/.pi-map.index.md
## role ## role
Core application package for the Headquarter API, providing configuration, database connectivity, structured logging, and FastAPI application initialization. Core API application package that initializes and configures the Headquarter FastAPI backend with database, authentication, logging, and middleware infrastructure.
## files ## files
- __init__.py | Marks the directory as a Python package for the Headquarter API. - __init__.py | Marks the directory as a Python package for the Headquarter API.
- config.py | Defines application configuration settings with environment-based overrides using Pydantic, including database URLs, service domains, OAuth/Authentik integration, JWT/session settings, and computed properties for environment-specific behavior. | exp: class:Settings, func:build_database_url(user: str, password: str, host: str, port: int, database: str) → str | dep: pydantic, pydantic_settings - config.py | Defines application configuration settings with environment-based overrides using Pydantic, including database URLs, service domains, OAuth/Authentik integration, JWT/session settings, and computed properties for environment-specific behavior. | exp: class:Settings, func:build_database_url(user: str, password: str, host: str, port: int, database: str) → str | dep: pydantic, pydantic_settings
@@ -12,7 +12,7 @@ Core application package for the Headquarter API, providing configuration, datab
- logging_config.py | Configures structured JSON logging with correlation ID injection, custom formatters, and HTTP request/exception middleware for a FastAPI application. | exp: class:CorrelationIdFilter, method:filter(self, record: logging.LogRecord) → bool, call:get_correlation_id, class:JSONFormatter, method:format(self, record: logging.LogRecord) → str, call:self.formatTime, call:record.getMessage, call:getattr, call:self.formatException, call:json.dumps, method:formatTime(self, record: logging.LogRecord, datefmt) → str, call:time.strftime, call:time.gmtime, class:RequestLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:time.time, call:logger.info, call:call_next, call:int, call:logger.error, call:type, call:traceback.format_exc, class:ExceptionLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:call_next, call:logger.critical, call:traceback.format_exc, func:configure_logging(level) → None, call:JSONFormatter, call:logging.StreamHandler, call:console_handler.setFormatter, call:console_handler.addFilter, call:CorrelationIdFilter, call:root_logger.setLevel, call:logging.getLogger("uvicorn").setLevel, call:logging.getLogger("uvicorn.access").setLevel, call:logging.getLogger("sqlalchemy.engine").setLevel, call:logger.info, call:logging.getLevelName | dep: json, logging, sys, time, traceback, collections.abc, fastapi, starlette.middleware.base, src.services.shared.correlation - logging_config.py | Configures structured JSON logging with correlation ID injection, custom formatters, and HTTP request/exception middleware for a FastAPI application. | exp: class:CorrelationIdFilter, method:filter(self, record: logging.LogRecord) → bool, call:get_correlation_id, class:JSONFormatter, method:format(self, record: logging.LogRecord) → str, call:self.formatTime, call:record.getMessage, call:getattr, call:self.formatException, call:json.dumps, method:formatTime(self, record: logging.LogRecord, datefmt) → str, call:time.strftime, call:time.gmtime, class:RequestLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:time.time, call:logger.info, call:call_next, call:int, call:logger.error, call:type, call:traceback.format_exc, class:ExceptionLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:call_next, call:logger.critical, call:traceback.format_exc, func:configure_logging(level) → None, call:JSONFormatter, call:logging.StreamHandler, call:console_handler.setFormatter, call:console_handler.addFilter, call:CorrelationIdFilter, call:root_logger.setLevel, call:logging.getLogger("uvicorn").setLevel, call:logging.getLogger("uvicorn.access").setLevel, call:logging.getLogger("sqlalchemy.engine").setLevel, call:logger.info, call:logging.getLevelName | dep: json, logging, sys, time, traceback, collections.abc, fastapi, starlette.middleware.base, src.services.shared.correlation
- main.py | Initializes and configures a FastAPI application for the "Headquarter API" with database setup, middleware, routing, and background services. | exp: func:_sanitize_validation_errors(errors), call:error.get, call:str, call:ctx.items, call:isinstance, call:type, call:sanitized.append, func:validation_exception_handler(request: Request, exc: RequestValidationError), call:exc.errors, call:logger.warning, call:_sanitize_validation_errors, call:JSONResponse, func:on_startup(), call:logger.info, call:init_database, call:logger.error, call:sys.exit, call:_health_monitor.start, call:seed_builtin_tool_types, func:on_shutdown(), call:logger.info, call:_health_monitor.stop | dep: logging, os, fastapi, fastapi.exceptions, fastapi.middleware.cors, fastapi.responses, fastapi.staticfiles, src.api.config, src.api.project, src.api.system, src.api.tool, src.api.user, src.api.workspace, src.config, src.models, src.database, src.logging_config, src.seeds.builtin_tool_types, src.services.instance, src.services.shared, sys, src.api.* - main.py | Initializes and configures a FastAPI application for the "Headquarter API" with database setup, middleware, routing, and background services. | exp: func:_sanitize_validation_errors(errors), call:error.get, call:str, call:ctx.items, call:isinstance, call:type, call:sanitized.append, func:validation_exception_handler(request: Request, exc: RequestValidationError), call:exc.errors, call:logger.warning, call:_sanitize_validation_errors, call:JSONResponse, func:on_startup(), call:logger.info, call:init_database, call:logger.error, call:sys.exit, call:_health_monitor.start, call:seed_builtin_tool_types, func:on_shutdown(), call:logger.info, call:_health_monitor.stop | dep: logging, os, fastapi, fastapi.exceptions, fastapi.middleware.cors, fastapi.responses, fastapi.staticfiles, src.api.config, src.api.project, src.api.system, src.api.tool, src.api.user, src.api.workspace, src.config, src.models, src.database, src.logging_config, src.seeds.builtin_tool_types, src.services.instance, src.services.shared, sys, src.api.*
## arch ## arch
Layered architecture with environment-based Pydantic config, async SQLAlchemy with retry patterns, structured JSON logging with correlation ID tracking, and modular FastAPI setup with middleware pipeline. Layered architecture using Pydantic for environment-based configuration, async SQLAlchemy with Alembic migrations, structured JSON logging with correlation IDs, and FastAPI middleware/routing pattern for a service-oriented backend.
## tags ## tags
src, database, logging, call:logger.info, api, middleware, fastapi, filter src, database, logging, call:logger.info, api, middleware, fastapi, filter
## symbols ## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/api/src/api dir: apps/api/src/api
## role ## role
Defines shared API infrastructure and reusable validation utilities for the API layer. Defines shared API infrastructure including reusable Pydantic validators for consistent input validation across API endpoints.
## parent ## parent
index: apps/api/src/.pi-map.index.md index: apps/api/src/.pi-map.index.md
map: apps/api/src/.pi-map.md map: apps/api/src/.pi-map.md
+2 -2
View File
@@ -4,12 +4,12 @@ dir: apps/api/src/api
index: apps/api/src/api/.pi-map.index.md index: apps/api/src/api/.pi-map.index.md
## role ## role
Defines shared API infrastructure and reusable validation utilities for the API layer. Defines shared API infrastructure including reusable Pydantic validators for consistent input validation across API endpoints.
## files ## files
- __init__.py | Marks the directory as a Python package for API routers. - __init__.py | Marks the directory as a Python package for API routers.
- shared_validators.py | Provides reusable Pydantic validator functions for API schema validation including mount paths, files, environment variables, and volume mounts. | exp: func:validate_mount_path(v: str | None) → str | None, call:v.startswith, raise:ValueError, func:validate_files(v: dict | None, max_size_bytes) → dict | None, call:v.items, call:path.startswith, call:len, call:content.encode, raise:ValueError, func:validate_env_vars(v: dict | None) → dict | None, call:isinstance, raise:ValueError, func:validate_volumes(v: list | None) → list | None, call:isinstance, call:enumerate, raise:ValueError - shared_validators.py | Provides reusable Pydantic validator functions for API schema validation including mount paths, files, environment variables, and volume mounts. | exp: func:validate_mount_path(v: str | None) → str | None, call:v.startswith, raise:ValueError, func:validate_files(v: dict | None, max_size_bytes) → dict | None, call:v.items, call:path.startswith, call:len, call:content.encode, raise:ValueError, func:validate_env_vars(v: dict | None) → dict | None, call:isinstance, raise:ValueError, func:validate_volumes(v: list | None) → list | None, call:isinstance, call:enumerate, raise:ValueError
## arch ## arch
Modular package structure with shared Pydantic validators for cross-cutting schema validation concerns. Modular utility package with functional validation helpers using Pydantic for declarative schema enforcement.
## tags ## tags
validate, raise:value, error, call:isinstance, mount, api, init, path validate, raise:value, error, call:isinstance, mount, api, init, path
## symbols ## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/api/src/api/workspace dir: apps/api/src/api/workspace
## role ## role
Provides FastAPI REST API endpoints for workspace management, including CRUD operations, file access, git version control, and tool instance management within user-scoped workspaces. Provides FastAPI REST endpoints for workspace management, file operations, git integration, and tool instance management within user workspaces.
## parent ## parent
index: apps/api/src/api/.pi-map.index.md index: apps/api/src/api/.pi-map.index.md
map: apps/api/src/api/.pi-map.md map: apps/api/src/api/.pi-map.md
+3 -3
View File
@@ -4,15 +4,15 @@ dir: apps/api/src/api/workspace
index: apps/api/src/api/workspace/.pi-map.index.md index: apps/api/src/api/workspace/.pi-map.index.md
## role ## role
Provides FastAPI REST API endpoints for workspace management, including CRUD operations, file access, git version control, and tool instance management within user-scoped workspaces. Provides FastAPI REST endpoints for workspace management, file operations, git integration, and tool instance management within user workspaces.
## files ## files
- __init__.py | Aggregates and re-exports workspace API router modules for centralized access | dep: src.api.workspace.workspace_files, src.api.workspace.workspace_git, src.api.workspace.workspace_instances, src.api.workspace.workspaces - __init__.py | Aggregates and re-exports workspace API router modules for centralized access | dep: src.api.workspace.workspace_files, src.api.workspace.workspace_git, src.api.workspace.workspace_instances, src.api.workspace.workspaces
- workspace_files.py | Provides FastAPI endpoints for listing, reading, and writing files within user workspaces with optional git commit integration. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:list_files(workspace_id: uuid.UUID, path, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.list_directory, raise:HTTPException, func:get_file_content(workspace_id: uuid.UUID, path: str, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.read_file, raise:HTTPException, func:write_file(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:FileService, call:data.get("path", "").strip, call:data.get("message", "").strip, call:service.write_file, call:GitOperations, call:git.commit, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.shared.file_service, sqlalchemy, src.services.git.git_operations - workspace_files.py | Provides FastAPI endpoints for listing, reading, and writing files within user workspaces with optional git commit integration. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:list_files(workspace_id: uuid.UUID, path, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.list_directory, raise:HTTPException, func:get_file_content(workspace_id: uuid.UUID, path: str, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.read_file, raise:HTTPException, func:write_file(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:FileService, call:data.get("path", "").strip, call:data.get("message", "").strip, call:service.write_file, call:GitOperations, call:git.commit, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.shared.file_service, sqlalchemy, src.services.git.git_operations
- workspace_git.py | Provides FastAPI REST endpoints for git operations (status, branches, commit, push, pull, fetch, checkout, history) scoped to user workspaces. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:git_status(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.status, raise:HTTPException, func:git_branches(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.branches, raise:HTTPException, func:git_commit(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("message", "").strip, call:GitOperations, call:git.commit, raise:HTTPException, func:git_push(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.push, raise:HTTPException, func:git_pull(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.pull, raise:HTTPException, func:git_fetch(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.fetch, raise:HTTPException, func:git_checkout(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("branch", "").strip, call:GitOperations, call:git.checkout, call:session.commit, raise:HTTPException, func:git_history(workspace_id: uuid.UUID, path, limit, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.history, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.git.git_operations, sqlalchemy - workspace_git.py | Provides FastAPI REST endpoints for git operations (status, branches, commit, push, pull, fetch, checkout, history) scoped to user workspaces. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:git_status(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.status, raise:HTTPException, func:git_branches(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.branches, raise:HTTPException, func:git_commit(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("message", "").strip, call:GitOperations, call:git.commit, raise:HTTPException, func:git_push(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.push, raise:HTTPException, func:git_pull(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.pull, raise:HTTPException, func:git_fetch(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.fetch, raise:HTTPException, func:git_checkout(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("branch", "").strip, call:GitOperations, call:git.checkout, call:session.commit, raise:HTTPException, func:git_history(workspace_id: uuid.UUID, path, limit, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.history, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.git.git_operations, sqlalchemy
- workspace_instances.py | FastAPI router providing endpoints to create and list tool instances associated with a specific workspace. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:create_workspace_instance(workspace_id: uuid.UUID, data: CreateWorkspaceInstanceRequest, user_id, session) → dict, call:_get_workspace, call:session.get, call:CreateInstanceRequest, call:str, call:create_tool_instance, call:instance.created_at.isoformat, raise:HTTPException, func:list_workspace_instances(workspace_id: uuid.UUID, user_id, session) → list[dict], call:_get_workspace, call:session.execute, call:select(ToolInstance) .where(ToolInstance.workspace_id == workspace_id) .order_by, call:ToolInstance.created_at.desc, call:result.scalars().all, call:str, call:i.created_at.isoformat | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.schemas.tool, src.services.tool.instance_service - workspace_instances.py | FastAPI router providing endpoints to create and list tool instances associated with a specific workspace. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:create_workspace_instance(workspace_id: uuid.UUID, data: CreateWorkspaceInstanceRequest, user_id, session) → dict, call:_get_workspace, call:session.get, call:CreateInstanceRequest, call:str, call:create_tool_instance, call:instance.created_at.isoformat, raise:HTTPException, func:list_workspace_instances(workspace_id: uuid.UUID, user_id, session) → list[dict], call:_get_workspace, call:session.execute, call:select(ToolInstance) .where(ToolInstance.workspace_id == workspace_id) .order_by, call:ToolInstance.created_at.desc, call:result.scalars().all, call:str, call:i.created_at.isoformat | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.schemas.tool, src.services.tool.instance_service
- workspaces.py | Provides FastAPI CRUD endpoints for managing Git repository workspaces with nested and top-level URL structures, including listing, creating, updating, deleting, and syncing workspaces with instance count tracking. | exp: func:list_all_workspaces(user_id, session) → list[dict], call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, GitRepository.name.label("repo_name"), GitRepository.project_id, GitRepository.ssh_key_id.label("repo_ssh_key_id"), instance_count.label("instance_count"), ) .join(GitRepository, Workspace.repo_id == GitRepository.id) .where(Workspace.user_id == user_id) .order_by, call:GitRepository.name.label, call:GitRepository.ssh_key_id.label, call:instance_count.label, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:delete_workspace_top_level(workspace_id: uuid.UUID, force, user_id, session) → dict, call:session.get, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:create_workspace_top_level(data: dict, user_id, session) → dict, call:data.get("repo_id", "").strip, call:uuid.UUID, call:session.get, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:list_workspaces(project_id: uuid.UUID, repo_id: uuid.UUID, user_id, session) → list[dict], call:_get_repo, call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, instance_count.label("instance_count"), ) .where(Workspace.repo_id == repo_id) .order_by, call:instance_count.label, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:create_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:get_workspace_detail(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:session.execute, call:select(func.count(ToolInstance.id)).where, call:func.count, call:result.scalar, call:str, call:workspace.last_sync_at.isoformat, call:workspace.created_at.isoformat, call:workspace.updated_at.isoformat, func:update_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:data.get("name", "").strip, call:data.get("branch", "").strip, call:session.commit, call:session.rollback, call:logger.error, call:str, raise:HTTPException, func:delete_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, force, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:sync_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.sync, call:session.commit, call:workspace.last_sync_at.isoformat, raise:HTTPException, func:_get_repo(session: AsyncSession, repo_id: uuid.UUID, project_id: uuid.UUID, user_id: uuid.UUID) → GitRepository, call:session.execute, call:select(GitRepository) .where( GitRepository.id == repo_id, GitRepository.project_id == project_id, ) .options, call:selectinload, call:result.scalar_one_or_none, raise:HTTPException, func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, repo_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException | dep: logging, uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, sqlalchemy.orm, src.auth.dependencies, src.models, src.services.shared.workspace_manager - workspaces.py | FastAPI router providing CRUD endpoints for workspace management with nested and top-level URL structures, including listing, creating, updating, deleting, and syncing workspaces tied to Git repositories. | exp: func:list_all_workspaces(user_id, session) → list[dict], call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, GitRepository, instance_count.label("instance_count"), ) .join(GitRepository, Workspace.repo_id == GitRepository.id) .options(selectinload(GitRepository.project)) .where(Workspace.user_id == user_id) .order_by, call:instance_count.label, call:selectinload, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:delete_workspace_top_level(workspace_id: uuid.UUID, force, user_id, session) → dict, call:session.get, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:create_workspace_top_level(data: dict, user_id, session) → dict, call:data.get("repo_id", "").strip, call:uuid.UUID, call:session.get, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:list_workspaces(project_id: uuid.UUID, repo_id: uuid.UUID, user_id, session) → list[dict], call:_get_repo, call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, instance_count.label("instance_count"), ) .where(Workspace.repo_id == repo_id) .order_by, call:instance_count.label, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:create_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:get_workspace_detail(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:session.execute, call:select(func.count(ToolInstance.id)).where, call:func.count, call:result.scalar, call:str, call:workspace.last_sync_at.isoformat, call:workspace.created_at.isoformat, call:workspace.updated_at.isoformat, func:update_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:data.get("name", "").strip, call:data.get("branch", "").strip, call:session.commit, call:session.rollback, call:logger.error, call:str, raise:HTTPException, func:delete_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, force, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:sync_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.sync, call:session.commit, call:workspace.last_sync_at.isoformat, raise:HTTPException, func:_get_repo(session: AsyncSession, repo_id: uuid.UUID, project_id: uuid.UUID, user_id: uuid.UUID) → GitRepository, call:session.execute, call:select(GitRepository) .where( GitRepository.id == repo_id, GitRepository.project_id == project_id, ) .options, call:selectinload, call:result.scalar_one_or_none, raise:HTTPException, func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, repo_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException | dep: logging, uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, sqlalchemy.orm, src.auth.dependencies, src.models, src.services.shared.workspace_manager
## arch ## arch
Modular router-based architecture using FastAPI with domain-driven decomposition into separate modules (files, git, instances, workspaces), nested URL routing for hierarchical resources, and optional git integration for file operations. Modular FastAPI router pattern with domain-driven separation (files, git, instances, workspaces) using nested URL structures and git-backed workspace synchronization.
## tags ## tags
workspace, get, raise:httpexception, call:, at.isoformat, git, call:select, call:data.get workspace, get, raise:httpexception, call:, at.isoformat, git, call:select, call:data.get
## symbols ## symbols
+8 -9
View File
@@ -36,12 +36,11 @@ async def list_all_workspaces(
result = await session.execute( result = await session.execute(
select( select(
Workspace, Workspace,
GitRepository.name.label("repo_name"), GitRepository,
GitRepository.project_id,
GitRepository.ssh_key_id.label("repo_ssh_key_id"),
instance_count.label("instance_count"), instance_count.label("instance_count"),
) )
.join(GitRepository, Workspace.repo_id == GitRepository.id) .join(GitRepository, Workspace.repo_id == GitRepository.id)
.options(selectinload(GitRepository.project))
.where(Workspace.user_id == user_id) .where(Workspace.user_id == user_id)
.order_by(Workspace.created_at.desc()) .order_by(Workspace.created_at.desc())
) )
@@ -51,11 +50,11 @@ async def list_all_workspaces(
{ {
"id": str(ws.id), "id": str(ws.id),
"name": ws.name, "name": ws.name,
"repo_id": str(ws.repo_id), "repo_id": str(repo.id),
"repo_name": repo_name or "", "repo_name": repo.name or "",
"repo_ssh_key_id": str(ssh_key_id) if ssh_key_id else None, "repo_ssh_key_id": str(repo.ssh_key_id) if repo.ssh_key_id else None,
"project_id": str(project_id) if project_id else "", "project_id": str(repo.project_id) if repo.project_id else "",
"project_name": "", "project_name": repo.project.name if repo.project else "",
"user_id": str(ws.user_id), "user_id": str(ws.user_id),
"branch": ws.branch, "branch": ws.branch,
"path": ws.path, "path": ws.path,
@@ -65,7 +64,7 @@ async def list_all_workspaces(
"updated_at": ws.updated_at.isoformat() if ws.updated_at else None, "updated_at": ws.updated_at.isoformat() if ws.updated_at else None,
"instance_count": count or 0, "instance_count": count or 0,
} }
for ws, repo_name, project_id, ssh_key_id, count in rows for ws, repo, count in rows
] ]
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/web dir: apps/web
## role ## role
Frontend web application for the headquarters system, providing a browser-based UI with code editing, terminal, and routing capabilities. Frontend web application providing a React-based UI with code editing, terminal, and routing capabilities for the "headquarter" project.
## parent ## parent
index: apps/.pi-map.index.md index: apps/.pi-map.index.md
map: apps/.pi-map.md map: apps/.pi-map.md
+2 -2
View File
@@ -4,7 +4,7 @@ dir: apps/web
index: apps/web/.pi-map.index.md index: apps/web/.pi-map.index.md
## role ## role
Frontend web application for the headquarters system, providing a browser-based UI with code editing, terminal, and routing capabilities. Frontend web application providing a React-based UI with code editing, terminal, and routing capabilities for the "headquarter" project.
## files ## files
- .env.example | Template file defining example environment variables for frontend API and application URL configuration - .env.example | Template file defining example environment variables for frontend API and application URL configuration
- .eslintrc.cjs | Configures ESLint for a TypeScript browser project with modern ECMAScript module support | dep: @typescript-eslint/parser, @typescript-eslint/eslint-plugin, eslint - .eslintrc.cjs | Configures ESLint for a TypeScript browser project with modern ECMAScript module support | dep: @typescript-eslint/parser, @typescript-eslint/eslint-plugin, eslint
@@ -16,7 +16,7 @@ Frontend web application for the headquarters system, providing a browser-based
- tsconfig.json | TypeScript configuration file for a React project using Vite with modern ES2020 target and bundler module resolution | dep: typescript, react, vite - tsconfig.json | TypeScript configuration file for a React project using Vite with modern ES2020 target and bundler module resolution | dep: typescript, react, vite
- vite.config.ts | Configures Vite build tool for a React project with custom dev server port and Vitest test settings. | dep: vite, @vitejs/plugin-react - vite.config.ts | Configures Vite build tool for a React project with custom dev server port and Vitest test settings. | dep: vite, @vitejs/plugin-react
## arch ## arch
Modern React SPA built with Vite and TypeScript, containerized with multi-stage Docker/nginx deployment, using ES modules with Vitest for testing. Modern React SPA built with Vite and TypeScript, using nginx for production serving with client-side routing, multi-stage Docker deployment, and Vitest for testing.
## tags ## tags
react, eslint, vite, typescript, dom, application, nginx, web react, eslint, vite, typescript, dom, application, nginx, web
## symbols ## symbols
+1 -1
View File
@@ -10,7 +10,7 @@ Frontend web application entry point and core infrastructure for a React-based c
- router.tsx | Defines the React Router configuration for a web application with protected routes, nested layouts, and redirects. | exp: AppRouter | dep: react-router-dom, ./components/app-shell, ./components/protected-route, ./pages/DashboardPage, ./pages/PlaceholderPage, ./pages/ProfilePage, ./pages/ProjectsPage, ./pages/GitRepositoriesPage, ./pages/GitHistoryPage, ./pages/ProjectSettingsPage, ./pages/SettingsPage, ./pages/TerminalPage, ./pages/ToolWorkshopPage, ./pages/SshKeysPage, ./pages/ConfigProfilesPage, ./pages/SessionsPage, ./pages/WorkspacesPage, ./pages/WorkspaceDetailPage - router.tsx | Defines the React Router configuration for a web application with protected routes, nested layouts, and redirects. | exp: AppRouter | dep: react-router-dom, ./components/app-shell, ./components/protected-route, ./pages/DashboardPage, ./pages/PlaceholderPage, ./pages/ProfilePage, ./pages/ProjectsPage, ./pages/GitRepositoriesPage, ./pages/GitHistoryPage, ./pages/ProjectSettingsPage, ./pages/SettingsPage, ./pages/TerminalPage, ./pages/ToolWorkshopPage, ./pages/SshKeysPage, ./pages/ConfigProfilesPage, ./pages/SessionsPage, ./pages/WorkspacesPage, ./pages/WorkspaceDetailPage
- types.ts | Defines TypeScript type definitions for user sessions, projects, repositories, and workspaces in an application. | exp: SessionUser, SessionPayload, Project, WorkspaceSummary, RepositorySummary, ProjectWithRepos - types.ts | Defines TypeScript type definitions for user sessions, projects, repositories, and workspaces in an application. | exp: SessionUser, SessionPayload, Project, WorkspaceSummary, RepositorySummary, ProjectWithRepos
## arch ## arch
Provider-composition pattern with React Router v6 nested route declarations, centralized type definitions, and layered authentication/session management wrapping a protected route hierarchy. Modular React SPA using React Router v6 with nested route layouts, protected route guards via authentication context, and centralized TypeScript domain models for session/workspace/project entities.
## tags ## tags
pages, styles, css, router, react, session, dom, project pages, styles, css, router, react, session, dom, project
## symbols ## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/web/src/components dir: apps/web/src/components
## role ## role
Provides foundational, reusable React UI components and utilities for rendering layouts, interactive elements, data states, icons, code editing, routing guards, and toast notifications across the web application. Provides reusable, accessible UI components and utilities for rendering the application shell, data states, icons, code display, notifications, and route protection in a React web application.
## parent ## parent
index: apps/web/src/.pi-map.index.md index: apps/web/src/.pi-map.index.md
map: apps/web/src/.pi-map.md map: apps/web/src/.pi-map.md
+3 -3
View File
@@ -4,9 +4,9 @@ dir: apps/web/src/components
index: apps/web/src/components/.pi-map.index.md index: apps/web/src/components/.pi-map.index.md
## role ## role
Provides foundational, reusable React UI components and utilities for rendering layouts, interactive elements, data states, icons, code editing, routing guards, and toast notifications across the web application. Provides reusable, accessible UI components and utilities for rendering the application shell, data states, icons, code display, notifications, and route protection in a React web application.
## files ## files
- app-shell.tsx | Renders the main application shell layout with navigation, session management, and responsive mobile/desktop views for a React Router-based app. | exp: AppShell | dep: react-router-dom, ../api/sessions, ../hooks/use-theme, ../state/auth, ../state/sessions, ../hooks/use-mobile-viewport, ../state/events, ../state/toast, ../state/notifications, ./features/notification/event-toast-bridge, ./features/notification/notification-center, ./icon, ./features/mobile/mobile-nav, ./features/tool/start-tool-fab, ../utils/icons - app-shell.tsx | Renders the main application shell layout with navigation, header, session management, and mobile-responsive behavior for a React Router-based SPA. | exp: AppShell | dep: react-router-dom, ../api/sessions, ../hooks/use-theme, ../state/auth, ../state/sessions, ../hooks/use-mobile-viewport, ../state/events, ../state/toast, ../state/notifications, ./features/notification/event-toast-bridge, ./features/notification/notification-center, ./icon, ./features/mobile/mobile-nav, ./features/tool/start-tool-fab, ../utils/icons
- code-editor.tsx | A React component that renders a syntax-highlighted code editor with line numbers using react-simple-code-editor. | exp: CodeEditor | dep: react, react-simple-code-editor, ../utils/language - code-editor.tsx | A React component that renders a syntax-highlighted code editor with line numbers using react-simple-code-editor. | exp: CodeEditor | dep: react, react-simple-code-editor, ../utils/language
- data-states.tsx | Provides reusable React components for displaying loading, error, and empty data states in a UI. | exp: LoadingState, ErrorState, EmptyState | dep: ./icon, icon - data-states.tsx | Provides reusable React components for displaying loading, error, and empty data states in a UI. | exp: LoadingState, ErrorState, EmptyState | dep: ./icon, icon
- icon.tsx | Provides a centralized, type-safe React icon component that maps semantic names to Phosphor icons with configurable size, weight, color, and accessibility attributes. | exp: IconName, IconProps, Icon | dep: react, @phosphor-icons/react - icon.tsx | Provides a centralized, type-safe React icon component that maps semantic names to Phosphor icons with configurable size, weight, color, and accessibility attributes. | exp: IconName, IconProps, Icon | dep: react, @phosphor-icons/react
@@ -17,7 +17,7 @@ Provides foundational, reusable React UI components and utilities for rendering
- toast-rules.test.ts | Unit tests for mapping instance events to toast notification categories and severities | dep: vitest, ./toast-rules, ../types/events - toast-rules.test.ts | Unit tests for mapping instance events to toast notification categories and severities | dep: vitest, ./toast-rules, ../types/events
- toast-rules.ts | Maps instance events to toast notifications with deduplication logic to prevent spam | exp: func:mapEventToCategory(event: InstanceEventPayload) → string, call:event.event.startsWith, func:mapEventToSeverity(event: InstanceEventPayload) → "info" | "warning" | "error" | "success", func:handleEventToast(event: InstanceEventPayload) → void, call:shouldShowToast, call:toast.info, call:toast.success, call:toast.warning, call:toast.error, func:clearToastDedup() → void, call:lastToastTime.clear | dep: ../state/toast, ../types/events, toast state module, InstanceEventPayload type - toast-rules.ts | Maps instance events to toast notifications with deduplication logic to prevent spam | exp: func:mapEventToCategory(event: InstanceEventPayload) → string, call:event.event.startsWith, func:mapEventToSeverity(event: InstanceEventPayload) → "info" | "warning" | "error" | "success", func:handleEventToast(event: InstanceEventPayload) → void, call:shouldShowToast, call:toast.info, call:toast.success, call:toast.warning, call:toast.error, func:clearToastDedup() → void, call:lastToastTime.clear | dep: ../state/toast, ../types/events, toast state module, InstanceEventPayload type
## arch ## arch
Component-based architecture with functional React patterns, custom hooks for state management, centralized icon mapping with type safety, HOC-like route guards, and utility modules for cross-cutting concerns (toast rules, data states) with co-located test files. Component-based React architecture with functional components, composition patterns, separation of concerns (presentation vs logic), and test coverage for critical utilities; integrates third-party libraries (Phosphor icons, react-simple-code-editor) and React Router for SPA navigation.
## tags ## tags
toast, icon, react, state, code, loading, event, editor toast, icon, react, state, code, loading, event, editor
## symbols ## symbols
+2 -2
View File
@@ -22,9 +22,9 @@ const NAV_ITEMS: {
badge?: "sessions"; badge?: "sessions";
}[] = [ }[] = [
{ to: "/", label: "Home", icon: "dashboard" }, { to: "/", label: "Home", icon: "dashboard" },
{ to: "/sessions", label: "Sessions", icon: "terminal", badge: "sessions" },
{ to: "/workspaces", label: "Workspaces", icon: "folder" },
{ to: "/projects", label: "Projects", icon: "projects" }, { to: "/projects", label: "Projects", icon: "projects" },
{ to: "/workspaces", label: "Workspaces", icon: "folder" },
{ to: "/sessions", label: "Sessions", icon: "terminal", badge: "sessions" },
{ to: "/tool-workshop", label: "Tool Workshop", icon: "settings" }, { to: "/tool-workshop", label: "Tool Workshop", icon: "settings" },
{ to: "/config-profiles", label: "Config Profiles", icon: "folder" }, { to: "/config-profiles", label: "Config Profiles", icon: "folder" },
{ to: "/settings", label: "Settings", icon: "settings" }, { to: "/settings", label: "Settings", icon: "settings" },
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features dir: apps/web/src/components/features
## role ## role
Contains reusable UI components organized by feature domain for the web application. Contains reusable React components that implement specific user-facing features and functionality across the web application.
## parent ## parent
index: apps/web/src/components/.pi-map.index.md index: apps/web/src/components/.pi-map.index.md
map: apps/web/src/components/.pi-map.md map: apps/web/src/components/.pi-map.md
+2 -2
View File
@@ -4,10 +4,10 @@ dir: apps/web/src/components/features
index: apps/web/src/components/features/.pi-map.index.md index: apps/web/src/components/features/.pi-map.index.md
## role ## role
Contains reusable UI components organized by feature domain for the web application. Contains reusable React components that implement specific user-facing features and functionality across the web application.
## files ## files
## arch ## arch
Feature-based colocation pattern with domain-specific subdirectories grouping related components, likely using composition patterns with shared UI primitives. Feature-based component organization with domain-specific UI building blocks following React composition patterns, likely co-located with related hooks, utilities, or sub-components for each feature area.
## tags ## tags
- -
## symbols ## symbols
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/mobile dir: apps/web/src/components/features/mobile
## role ## role
Provides a complete set of mobile-optimized UI components for core application patterns including navigation, lists, detail views, editing, modals, and specialized terminal interfaces. Provides mobile-optimized UI components for core application features including navigation, data views, forms, modals, and specialized terminal interfaces.
## parent ## parent
index: apps/web/src/components/features/.pi-map.index.md index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md map: apps/web/src/components/features/.pi-map.md
@@ -4,22 +4,22 @@ dir: apps/web/src/components/features/mobile
index: apps/web/src/components/features/mobile/.pi-map.index.md index: apps/web/src/components/features/mobile/.pi-map.index.md
## role ## role
Provides a complete set of mobile-optimized UI components for core application patterns including navigation, lists, detail views, editing, modals, and specialized terminal interfaces. Provides mobile-optimized UI components for core application features including navigation, data views, forms, modals, and specialized terminal interfaces.
## files ## files
- mobile-action-sheet.tsx | Renders a mobile-optimized action sheet modal with title, configurable action buttons, and cancel option. | exp: MobileActionSheetItem, func:MobileActionSheet({ isOpen, onClose, title, actions, }: MobileActionSheetProps), call:useRef, call:useEffect, call:onClose, call:document.addEventListener, call:document.removeEventListener, call:e.stopPropagation, call:actions.map, call:action.onClick | dep: react, ../../icon, icon - mobile-action-sheet.tsx | Renders a mobile-optimized action sheet modal with title, configurable action buttons, and cancel option. | exp: MobileActionSheetItem, func:MobileActionSheet({ isOpen, onClose, title, actions, }: MobileActionSheetProps), call:useRef, call:useEffect, call:onClose, call:document.addEventListener, call:document.removeEventListener, call:e.stopPropagation, call:actions.map, call:action.onClick | dep: react, ../../icon, icon
- mobile-detail-view.tsx | A React component that renders a mobile-optimized detail view with a header (back button, title, edit/delete actions) and a field list supporting multiple value types (text, code, JSON, boolean). | exp: MobileDetailView | dep: ../../icon, react - mobile-detail-view.tsx | A React component that renders a mobile-optimized detail view with a header (back button, title, edit/delete actions) and a field list supporting multiple value types (text, code, JSON, boolean). | exp: MobileDetailView | dep: ../../icon, react
- mobile-edit-view.tsx | A React component that renders a mobile-optimized form for editing data with configurable field types and save/cancel actions. | exp: MobileEditView | dep: react - mobile-edit-view.tsx | A React component that renders a mobile-optimized form for editing data with configurable field types and save/cancel actions. | exp: MobileEditView | dep: react
- mobile-fab.tsx | Renders a floating action button component for mobile with an add icon and configurable click handler and label. | exp: MobileFAB | dep: ../../icon, react, icon - mobile-fab.tsx | Renders a floating action button component for mobile with an add icon and configurable click handler and label. | exp: MobileFAB | dep: ../../icon, react, icon
- mobile-list-view.tsx | Renders a mobile-optimized list view with optional search, custom item rendering, and empty state handling | exp: MobileListView | dep: react, ../../icon, ../../../utils/icons, icon - mobile-list-view.tsx | Renders a mobile-optimized list view with optional search, empty state, and customizable item rendering | exp: MobileListView | dep: react, ../../icon, ../../../utils/icons, icon
- mobile-nav.tsx | Renders a mobile navigation bar with route links and expandable bottom sheets for grouped navigation items. | exp: MobileNav | dep: react, react-router-dom, ../../icon, ../tool/tools-bottom-sheet, ./spaces-bottom-sheet, ../../../utils/icons - mobile-nav.tsx | Renders a mobile navigation bar with grouped items that open bottom sheets and standard items that use React Router links, including active state indicators and session count badges. | exp: MobileNav | dep: react, react-router-dom, ../../icon, ../tool/tools-bottom-sheet, ./spaces-bottom-sheet, ../../../utils/icons
- mobile-page-header.tsx | Renders a mobile-only page header with optional back navigation and custom actions. | exp: func:MobilePageHeader({ title, showBack = true, actions }: MobilePageHeaderProps), call:useNavigate, call:useMobileViewport, call:navigate | dep: react-router-dom, ../../../hooks/use-mobile-viewport, ../../icon, use-mobile-viewport hook, Icon component - mobile-page-header.tsx | Renders a mobile-only page header with optional back navigation and custom actions. | exp: func:MobilePageHeader({ title, showBack = true, actions }: MobilePageHeaderProps), call:useNavigate, call:useMobileViewport, call:navigate | dep: react-router-dom, ../../../hooks/use-mobile-viewport, ../../icon, use-mobile-viewport hook, Icon component
- mobile-terminal-header.tsx | Renders a mobile-responsive header for a terminal interface with navigation, title, connection status, font size controls, and close actions. | exp: MobileTerminalHeader | dep: react, ../../icon, icon - mobile-terminal-header.tsx | Renders a mobile-responsive header for a terminal interface with navigation, title, connection status, font size controls, and close actions. | exp: MobileTerminalHeader | dep: react, ../../icon, icon
- mobile-terminal-wrapper.tsx | Wraps a terminal component with mobile-specific UI including auto-hiding header, virtual keyboard handling, and special keys interface. | exp: MobileTerminalWrapper | dep: react, ../terminal/terminal, ./mobile-terminal-header, ../terminal/special-keys-strip, ../terminal/special-keys-panel, ../../../hooks/use-mobile-viewport, ../../../hooks/use-virtual-keyboard, ../../../hooks/use-auto-hide, ../../../hooks/use-special-keys - mobile-terminal-wrapper.tsx | Wraps a terminal component with mobile-specific UI including auto-hiding header, virtual keyboard handling, and special keys interface. | exp: MobileTerminalWrapper | dep: react, ../terminal/terminal, ./mobile-terminal-header, ../terminal/special-keys-strip, ../terminal/special-keys-panel, ../../../hooks/use-mobile-viewport, ../../../hooks/use-virtual-keyboard, ../../../hooks/use-auto-hide, ../../../hooks/use-special-keys
- spaces-bottom-sheet.tsx | Renders a mobile bottom sheet navigation menu for switching between "Projects" and "Workspaces" spaces with active state highlighting | exp: SpacesBottomSheet | dep: react-router-dom, ../../icon - spaces-bottom-sheet.tsx | Renders a mobile bottom sheet navigation menu for switching between "Projects" and "Workspaces" spaces with active state highlighting. | exp: SpacesBottomSheet | dep: react-router-dom, ../../icon, icon
## arch ## arch
Composable mobile-first component library using bottom sheets, action sheets, and floating action buttons as primary mobile interaction patterns, with feature-specific wrappers that handle mobile concerns like virtual keyboards and auto-hiding headers. Compositional React component library with feature-specific mobile adaptations, using bottom sheets/action sheets for mobile-native UX patterns, and wrapper components that inject mobile-specific behaviors (auto-hiding headers, virtual keyboard handling) into existing features.
## tags ## tags
mobile, terminal, sheet, view, react, icon, header, renders mobile, terminal, sheet, react, icon, view, header, renders
## symbols ## symbols
- MobileActionSheet - MobileActionSheet
- MobilePageHeader - MobilePageHeader
@@ -3,81 +3,86 @@ import { Icon } from "../../icon";
import type { IconName } from "../../../utils/icons"; import type { IconName } from "../../../utils/icons";
interface MobileListItem { interface MobileListItem {
id: string; id: string;
title: string; title: string;
subtitle?: string; subtitle?: string;
icon?: string; icon?: string;
status?: string; status?: string;
} }
interface MobileListViewProps { interface MobileListViewProps {
items: MobileListItem[]; items: MobileListItem[];
onItemClick: (id: string) => void; onItemClick: (id: string) => void;
onItemDelete?: (id: string) => void; onItemDelete?: (id: string) => void;
onItemDuplicate?: (id: string) => void; onItemDuplicate?: (id: string) => void;
emptyMessage?: string; emptyMessage?: string;
searchPlaceholder?: string; searchPlaceholder?: string;
onSearch?: (query: string) => void; onSearch?: (query: string) => void;
renderItem?: (item: MobileListItem) => ReactNode; renderItem?: (item: MobileListItem) => ReactNode;
} }
export const MobileListView: React.FC<MobileListViewProps> = ({ export const MobileListView: React.FC<MobileListViewProps> = ({
items, items,
onItemClick, onItemClick,
emptyMessage = "No items found", emptyMessage = "No items found",
searchPlaceholder = "Search...", searchPlaceholder = "Search...",
onSearch, onSearch,
renderItem, renderItem,
}) => { }) => {
return ( return (
<div className="mobile-list-view"> <div className="mobile-list-view">
{onSearch && ( {onSearch && (
<div className="mobile-list-search"> <div className="mobile-list-search">
<input <input
type="search" type="search"
placeholder={searchPlaceholder} placeholder={searchPlaceholder}
onChange={(e) => onSearch(e.target.value)} onChange={(e) => onSearch(e.target.value)}
className="mobile-list-search-input" className="mobile-list-search-input"
/> />
</div> </div>
)} )}
{items.length === 0 ? ( {items.length === 0 ? (
<div className="mobile-list-empty"> <div className="mobile-list-empty">
<Icon name="folder" size="lg" /> <Icon name="folder" size="lg" />
<p>{emptyMessage}</p> <p>{emptyMessage}</p>
</div> </div>
) : ( ) : (
<div className="mobile-list-items"> <div className="mobile-list-items">
{items.map((item) => ( {items.map((item) => (
<button <button
key={item.id} key={item.id}
className="mobile-list-item" className="mobile-list-item"
onClick={() => onItemClick(item.id)} onClick={() => onItemClick(item.id)}
type="button" type="button"
> >
{item.icon && ( {item.icon && (
<div className="mobile-list-item-icon"> <div className="mobile-list-item-icon">
<Icon name={item.icon as IconName} size="md" /> <Icon name={item.icon as IconName} size="md" />
</div> </div>
)} )}
{renderItem ? ( {renderItem ? (
renderItem(item) renderItem(item)
) : ( ) : (
<div className="mobile-list-item-content"> <div className="mobile-list-item-content">
<div className="mobile-list-item-title">{item.title}</div> <div className="mobile-list-item-title">{item.title}</div>
{item.subtitle && ( {item.subtitle && (
<div className="mobile-list-item-subtitle">{item.subtitle}</div> <div className="mobile-list-item-subtitle">
)} {item.subtitle}
</div> </div>
)} )}
<div className="mobile-list-item-actions" style={{ transform: "rotate(180deg)" }}> </div>
<Icon name="arrow-left" size="sm" /> )}
</div> <div
</button> className="mobile-list-item-actions"
))} style={{ transform: "rotate(180deg)" }}
</div> >
)} <Icon name="arrow-left" size="sm" />
</div> </div>
); </button>
))}
</div>
)}
</div>
);
}; };
@@ -6,97 +6,114 @@ import { SpacesBottomSheet } from "./spaces-bottom-sheet";
import type { IconName } from "../../../utils/icons"; import type { IconName } from "../../../utils/icons";
interface MobileNavProps { interface MobileNavProps {
sessionCount?: number; sessionCount?: number;
} }
interface NavItem { interface NavItem {
to: string; to: string;
label: string; label: string;
icon: IconName; icon: IconName;
isGroup?: boolean; isGroup?: boolean;
group?: "tools" | "spaces"; group?: "tools" | "spaces";
} }
const MOBILE_NAV_ITEMS: NavItem[] = [ const MOBILE_NAV_ITEMS: NavItem[] = [
{ to: "/", label: "Home", icon: "dashboard" }, { to: "/", label: "Home", icon: "dashboard" },
{ to: "/sessions", label: "Sessions", icon: "terminal" }, {
{ to: "/spaces", label: "Spaces", icon: "projects", isGroup: true, group: "spaces" }, to: "/spaces",
{ to: "/tools", label: "Tools", icon: "settings", isGroup: true, group: "tools" }, label: "Spaces",
{ to: "/settings", label: "Settings", icon: "settings" }, icon: "projects",
isGroup: true,
group: "spaces",
},
{ to: "/sessions", label: "Sessions", icon: "terminal" },
{
to: "/tools",
label: "Tools",
icon: "settings",
isGroup: true,
group: "tools",
},
{ to: "/settings", label: "Settings", icon: "settings" },
]; ];
export const MobileNav: React.FC<MobileNavProps> = ({ sessionCount }) => { export const MobileNav: React.FC<MobileNavProps> = ({ sessionCount }) => {
const location = useLocation(); const location = useLocation();
const [toolsSheetOpen, setToolsSheetOpen] = useState(false); const [toolsSheetOpen, setToolsSheetOpen] = useState(false);
const [spacesSheetOpen, setSpacesSheetOpen] = useState(false); const [spacesSheetOpen, setSpacesSheetOpen] = useState(false);
const isToolsActive = const isToolsActive =
location.pathname === "/tool-workshop" || location.pathname === "/tool-workshop" ||
location.pathname === "/config-profiles"; location.pathname === "/config-profiles";
const isSpacesActive = const isSpacesActive =
location.pathname.startsWith("/projects") || location.pathname.startsWith("/projects") ||
location.pathname.startsWith("/workspaces"); location.pathname.startsWith("/workspaces");
const handleNavClick = (item: NavItem) => { const handleNavClick = (item: NavItem) => {
if (!item.isGroup) return; if (!item.isGroup) return;
if (item.group === "spaces") { if (item.group === "spaces") {
setSpacesSheetOpen(true); setSpacesSheetOpen(true);
} else { } else {
setToolsSheetOpen(true); setToolsSheetOpen(true);
} }
}; };
return ( return (
<> <>
<nav className="mobile-nav" role="navigation" aria-label="Mobile navigation"> <nav
{MOBILE_NAV_ITEMS.map((item) => { className="mobile-nav"
if (item.isGroup) { role="navigation"
const isActive = item.group === "spaces" ? isSpacesActive : isToolsActive; aria-label="Mobile navigation"
return ( >
<button {MOBILE_NAV_ITEMS.map((item) => {
key={item.to} if (item.isGroup) {
className={`mobile-nav-item ${isActive ? "active" : ""}`} const isActive =
onClick={() => handleNavClick(item)} item.group === "spaces" ? isSpacesActive : isToolsActive;
type="button" return (
> <button
<div className="mobile-nav-icon-wrapper"> key={item.to}
<Icon name={item.icon} size="md" /> className={`mobile-nav-item ${isActive ? "active" : ""}`}
</div> onClick={() => handleNavClick(item)}
<span className="mobile-nav-label">{item.label}</span> type="button"
</button> >
); <div className="mobile-nav-icon-wrapper">
} <Icon name={item.icon} size="md" />
</div>
<span className="mobile-nav-label">{item.label}</span>
</button>
);
}
return ( return (
<NavLink <NavLink
key={item.to} key={item.to}
to={item.to} to={item.to}
className={({ isActive }) => className={({ isActive }) =>
`mobile-nav-item ${isActive ? "active" : ""}` `mobile-nav-item ${isActive ? "active" : ""}`
} }
end={item.to === "/"} end={item.to === "/"}
> >
<div className="mobile-nav-icon-wrapper"> <div className="mobile-nav-icon-wrapper">
<Icon name={item.icon} size="md" /> <Icon name={item.icon} size="md" />
{item.to === "/sessions" && sessionCount ? ( {item.to === "/sessions" && sessionCount ? (
<span className="mobile-nav-badge">{sessionCount}</span> <span className="mobile-nav-badge">{sessionCount}</span>
) : null} ) : null}
</div> </div>
<span className="mobile-nav-label">{item.label}</span> <span className="mobile-nav-label">{item.label}</span>
</NavLink> </NavLink>
); );
})} })}
</nav> </nav>
<ToolsBottomSheet <ToolsBottomSheet
isOpen={toolsSheetOpen} isOpen={toolsSheetOpen}
onClose={() => setToolsSheetOpen(false)} onClose={() => setToolsSheetOpen(false)}
/> />
<SpacesBottomSheet <SpacesBottomSheet
isOpen={spacesSheetOpen} isOpen={spacesSheetOpen}
onClose={() => setSpacesSheetOpen(false)} onClose={() => setSpacesSheetOpen(false)}
/> />
</> </>
); );
}; };
@@ -2,61 +2,65 @@ import { useLocation, useNavigate } from "react-router-dom";
import { Icon } from "../../icon"; import { Icon } from "../../icon";
interface SpacesBottomSheetProps { interface SpacesBottomSheetProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
} }
const SPACES_ITEMS = [ const SPACES_ITEMS = [
{ to: "/projects", label: "Projects" }, { to: "/projects", label: "Projects" },
{ to: "/workspaces", label: "Workspaces" }, { to: "/workspaces", label: "Workspaces" },
]; ];
export const SpacesBottomSheet: React.FC<SpacesBottomSheetProps> = ({ export const SpacesBottomSheet: React.FC<SpacesBottomSheetProps> = ({
isOpen, isOpen,
onClose, onClose,
}) => { }) => {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
if (!isOpen) return null; if (!isOpen) return null;
const handleSelect = (to: string) => { const handleSelect = (to: string) => {
onClose(); onClose();
navigate(to); navigate(to);
}; };
return ( return (
<div <div
className="mobile-bottom-sheet-overlay" className="mobile-bottom-sheet-overlay"
onClick={onClose} onClick={onClose}
role="presentation" role="presentation"
> >
<div <div
className="mobile-bottom-sheet" className="mobile-bottom-sheet"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
role="dialog" role="dialog"
aria-label="Spaces menu" aria-label="Spaces menu"
> >
<div className="mobile-bottom-sheet-header"> <div className="mobile-bottom-sheet-header">
<div className="mobile-bottom-sheet-handle" /> <div className="mobile-bottom-sheet-handle" />
<h3 className="mobile-bottom-sheet-title">Spaces</h3> <h3 className="mobile-bottom-sheet-title">Spaces</h3>
</div> </div>
<div className="mobile-bottom-sheet-content"> <div className="mobile-bottom-sheet-content">
{SPACES_ITEMS.map((item) => ( {SPACES_ITEMS.map((item) => (
<button <button
key={item.to} key={item.to}
className={`mobile-bottom-sheet-item ${ className={`mobile-bottom-sheet-item ${
location.pathname === item.to ? "active" : "" location.pathname === item.to ? "active" : ""
}`} }`}
onClick={() => handleSelect(item.to)} onClick={() => handleSelect(item.to)}
type="button" type="button"
> >
<span className="mobile-bottom-sheet-item-label">{item.label}</span> <span className="mobile-bottom-sheet-item-label">
{location.pathname === item.to && <Icon name="success" size="sm" />} {item.label}
</button> </span>
))} {location.pathname === item.to && (
</div> <Icon name="success" size="sm" />
</div> )}
</div> </button>
); ))}
</div>
</div>
</div>
);
}; };
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/terminal dir: apps/web/src/components/features/terminal
## role ## role
Provides cross-platform terminal UI components for desktop and mobile with session management, special key input, and xterm.js WebSocket connectivity. Provides a cross-platform terminal emulator interface with WebSocket backend connectivity, supporting multiple sessions, special key input, and responsive desktop/mobile layouts.
## parent ## parent
index: apps/web/src/components/features/.pi-map.index.md index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md map: apps/web/src/components/features/.pi-map.md
@@ -4,7 +4,7 @@ dir: apps/web/src/components/features/terminal
index: apps/web/src/components/features/terminal/.pi-map.index.md index: apps/web/src/components/features/terminal/.pi-map.index.md
## role ## role
Provides cross-platform terminal UI components for desktop and mobile with session management, special key input, and xterm.js WebSocket connectivity. Provides a cross-platform terminal emulator interface with WebSocket backend connectivity, supporting multiple sessions, special key input, and responsive desktop/mobile layouts.
## files ## files
- DesktopTerminalView.tsx | Renders a desktop-optimized terminal view with session tabs, fullscreen mode, font controls, and reset confirmation dialog | exp: DesktopTerminalView | dep: react, ./terminal, ./terminal-session-tabs, ../../../api/terminal, React, TerminalComponent, TerminalSessionTabs, TerminalSession type - DesktopTerminalView.tsx | Renders a desktop-optimized terminal view with session tabs, fullscreen mode, font controls, and reset confirmation dialog | exp: DesktopTerminalView | dep: react, ./terminal, ./terminal-session-tabs, ../../../api/terminal, React, TerminalComponent, TerminalSessionTabs, TerminalSession type
- MobileTerminalView.tsx | Renders a mobile-optimized terminal interface with toolbar, session tabs, terminal output, and special keys input controls. | exp: MobileTerminalView | dep: react, ./terminal, ./terminal-session-tabs, ../../icon, ./special-keys-strip, ./special-keys-panel, ../../../hooks/use-special-keys, ../../../api/terminal - MobileTerminalView.tsx | Renders a mobile-optimized terminal interface with toolbar, session tabs, terminal output, and special keys input controls. | exp: MobileTerminalView | dep: react, ./terminal, ./terminal-session-tabs, ../../icon, ./special-keys-strip, ./special-keys-panel, ../../../hooks/use-special-keys, ../../../api/terminal
@@ -12,9 +12,9 @@ Provides cross-platform terminal UI components for desktop and mobile with sessi
- special-keys-strip.tsx | Renders a strip of buttons for sending special keyboard keys (Esc, Tab, arrows, etc.) with optional modifier support for a terminal interface. | exp: SpecialKeysStrip | dep: react, ../../../hooks/use-special-keys, React, use-special-keys hook - special-keys-strip.tsx | Renders a strip of buttons for sending special keyboard keys (Esc, Tab, arrows, etc.) with optional modifier support for a terminal interface. | exp: SpecialKeysStrip | dep: react, ../../../hooks/use-special-keys, React, use-special-keys hook
- terminal-session-tabs.test.tsx | Tests a React component that renders interactive terminal session tabs with selection, close confirmation, renaming, creation limits, and status indicators. | dep: @testing-library/react, vitest, ./terminal-session-tabs - terminal-session-tabs.test.tsx | Tests a React component that renders interactive terminal session tabs with selection, close confirmation, renaming, creation limits, and status indicators. | dep: @testing-library/react, vitest, ./terminal-session-tabs
- terminal-session-tabs.tsx | Renders a tabbed interface for managing multiple terminal sessions with selection, creation, renaming, and close confirmation features. | exp: TerminalSessionInfo, TerminalSessionTabsProps, TerminalSessionTabs | dep: react, React - terminal-session-tabs.tsx | Renders a tabbed interface for managing multiple terminal sessions with selection, creation, renaming, and close confirmation features. | exp: TerminalSessionInfo, TerminalSessionTabsProps, TerminalSessionTabs | dep: react, React
- terminal.tsx | React component that renders an interactive xterm.js terminal with WebSocket connectivity, mobile touch support, flow control, heartbeat monitoring, and reconnection logic. | exp: TerminalProps, TerminalRef, TerminalComponent | dep: react, xterm, xterm-addon-fit, xterm-addon-web-links, xterm/css/xterm.css, ../../../hooks/use-special-keys, React, use-special-keys hook - terminal.tsx | A React component that renders an interactive terminal emulator using xterm.js, connecting to a WebSocket backend with features like mobile touch scrolling, font resizing, flow control, heartbeat monitoring, and automatic reconnection. | exp: TerminalProps, TerminalRef, TerminalComponent | dep: react, xterm, xterm-addon-fit, xterm-addon-web-links, xterm/css/xterm.css, ../../../hooks/use-special-keys
## arch ## arch
Component-based architecture with platform-specific view variants (Desktop/Mobile), shared session tabs state management, modular special key input panels, and a core terminal component handling WebSocket lifecycle with heartbeat/reconnection logic. Component-based architecture with platform-specific view variants (Desktop/Mobile), compound component pattern for terminal subsystems (tabs, special keys, xterm.js integration), and WebSocket-based real-time communication with reconnection and heartbeat monitoring.
## tags ## tags
terminal, special, keys, session, react, tabs, view, renders terminal, special, keys, session, react, tabs, view, renders
## symbols ## symbols
@@ -355,13 +355,41 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
// In normal mode xterm.js has a scrollable viewport; in alternate // In normal mode xterm.js has a scrollable viewport; in alternate
// screen (tmux/vim) there is no scrollback and the only way to // screen (tmux/vim) there is no scrollback and the only way to
// scroll is to send mouse-wheel protocol sequences to the // scroll is to send mouse-wheel protocol sequences to the
// application. We detect which situation we're in by checking // application. We detect the active buffer by reference equality
// whether the viewport has scrollable height. // (term.buffer.active === term.buffer.alternate) because the
// `type` string can be unreliable in some xterm.js versions.
let touchCleanup: (() => void) | undefined; let touchCleanup: (() => void) | undefined;
if (isMobile) { if (isMobile) {
let startY = 0; let startY = 0;
let startX = 0; let startX = 0;
let isScrolling = false; let isScrolling = false;
let debugOverlay: HTMLDivElement | null = null;
const showDebug = (msg: string) => {
if (!debugOverlay) {
debugOverlay = document.createElement("div");
debugOverlay.style.position = "fixed";
debugOverlay.style.bottom = "120px";
debugOverlay.style.left = "8px";
debugOverlay.style.right = "8px";
debugOverlay.style.zIndex = "9999";
debugOverlay.style.background = "rgba(0,0,0,0.8)";
debugOverlay.style.color = "#0f0";
debugOverlay.style.fontFamily = "monospace";
debugOverlay.style.fontSize = "11px";
debugOverlay.style.padding = "8px";
debugOverlay.style.borderRadius = "6px";
debugOverlay.style.pointerEvents = "none";
debugOverlay.style.whiteSpace = "pre-wrap";
debugOverlay.style.maxHeight = "120px";
debugOverlay.style.overflow = "auto";
document.body.appendChild(debugOverlay);
}
const line = `${new Date().toLocaleTimeString()} ${msg}`;
debugOverlay.textContent =
line +
"\n" +
debugOverlay.textContent?.split("\n").slice(0, 8).join("\n");
};
const onTouchStart = (e: TouchEvent) => { const onTouchStart = (e: TouchEvent) => {
if (e.touches.length === 1) { if (e.touches.length === 1) {
@@ -375,46 +403,49 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
const touch = e.touches[0]; const touch = e.touches[0];
const deltaY = startY - touch.clientY; const deltaY = startY - touch.clientY;
const deltaX = Math.abs(startX - touch.clientX); const deltaX = Math.abs(startX - touch.clientX);
showDebug(`move dy=${deltaY.toFixed(0)} dx=${deltaX.toFixed(0)} scrolling=${isScrolling}`);
// Decide early whether this is a vertical scroll gesture.
if (!isScrolling) { if (!isScrolling) {
if (Math.abs(deltaY) > deltaX && Math.abs(deltaY) > 4) { if (Math.abs(deltaY) > deltaX && Math.abs(deltaY) > 2) {
isScrolling = true; isScrolling = true;
} }
} }
if (isScrolling) { if (!isScrolling) return;
e.preventDefault();
const viewport = container.querySelector(
".xterm-viewport",
) as HTMLElement | null;
if (!viewport) return;
// If the viewport is scrollable, scroll it directly. // Take over the gesture so the browser never scrolls the
// Otherwise we are in alternate screen (tmux/vim) and must // page or the terminal canvas. We synthesize wheel events
// send SGR 1006 mouse-wheel protocol data. // and dispatch them into xterm.js, which already handles
const hasScrollback = viewport.scrollHeight > viewport.clientHeight; // both normal buffer scrolling and alternate-screen apps
if (hasScrollback) { // (tmux/vim) correctly.
viewport.scrollTop += deltaY; e.preventDefault();
} else {
const ws = wsRef.current; const target = container.querySelector(".xterm-viewport") as
if (ws?.readyState === WebSocket.OPEN && termRef.current) { | HTMLElement
// Use the cursor position as the wheel location so | null;
// tmux knows which pane to scroll. if (target) {
const buf = termRef.current.buffer.active; showDebug(`dispatch wheel deltaY=${deltaY.toFixed(0)}`);
const col = buf.cursorX + 1; const wheel = new WheelEvent("wheel", {
const row = buf.cursorY + 1; deltaY,
// SGR 1006: 64 = wheel-up, 65 = wheel-down deltaX: 0,
const btn = deltaY > 0 ? 64 : 65; deltaZ: 0,
ws.send(`\x1b[<${btn};${col};${row}M`); clientX: touch.clientX,
} clientY: touch.clientY,
} screenX: touch.screenX,
startY = touch.clientY; screenY: touch.screenY,
bubbles: true,
cancelable: true,
});
target.dispatchEvent(wheel);
} }
startY = touch.clientY;
}; };
const onTouchEnd = () => { const onTouchEnd = () => {
isScrolling = false; isScrolling = false;
}; };
container.addEventListener("touchstart", onTouchStart, { container.addEventListener("touchstart", onTouchStart, {
passive: true, passive: false,
capture: true, capture: true,
}); });
container.addEventListener("touchmove", onTouchMove, { container.addEventListener("touchmove", onTouchMove, {
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/workspace dir: apps/web/src/components/features/workspace
## role ## role
Provides React UI components for workspace management, navigation, and detail views in a web-based development environment. Provides the complete UI component suite for workspace management, including creation, detail viewing, file editing, Git operations, tool management, and settings.
## parent ## parent
index: apps/web/src/components/features/.pi-map.index.md index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md map: apps/web/src/components/features/.pi-map.md
@@ -4,9 +4,9 @@ dir: apps/web/src/components/features/workspace
index: apps/web/src/components/features/workspace/.pi-map.index.md index: apps/web/src/components/features/workspace/.pi-map.index.md
## role ## role
Provides React UI components for workspace management, navigation, and detail views in a web-based development environment. Provides the complete UI component suite for workspace management, including creation, detail viewing, file editing, Git operations, tool management, and settings.
## files ## files
- workspace-card.tsx | React card component that displays workspace information with status, metadata, and action buttons | exp: WorkspaceCardProps, func:WorkspaceCard({ workspace, loading = false, onStartTool, onSync, onDelete, }: WorkspaceCardProps), call:onStartTool, call:onSync, call:onDelete | dep: react-router-dom, ../../icon, ./workspace-instance-chips, ../../../types/workspace - workspace-card.tsx | React component that renders a card displaying workspace information with status, metadata, and action buttons. | exp: WorkspaceCardProps, func:WorkspaceCard({ workspace, loading = false, onStartTool, onSync, onDelete, }: WorkspaceCardProps), call:onStartTool, call:onSync, call:onDelete | dep: react-router-dom, ../../icon, ./workspace-instance-chips, ../../../types/workspace
- workspace-create-form.tsx | React form component for creating workspaces with cascading project/repository/branch selectors and support for both contextual and standalone modes | exp: WorkspaceCreateFormProps, func:WorkspaceCreateForm({ onSubmit, onCancel, defaultProjectId, defaultRepoId, }: WorkspaceCreateFormProps), call:Boolean, call:useState, call:useGitRepo, call:useEffect, call:git.branches.includes, call:setSelectedBranch, call:setIsNewBranch, call:useCallback, call:listProjects, call:setProjects, call:setSelectedProject, call:setError, call:setFetchingProjects, call:loadProjects, call:setRepos, call:setSelectedRepo, call:listRepositories, call:loadRepos, call:setNewBranchName, call:e.preventDefault, call:name.trim, call:newBranchName.trim, call:setSubmitting, call:createWorkspaceTopLevel, call:onSubmit, call:projects.map, call:repos.map, call:setName, call:handleBranchChange, call:git.branches.map | dep: react, ../../icon, ../../../api/projects, ../../../api/git-repositories, ../../../api/workspaces, ../../../hooks/use-git-repo, ../../../types, icon, api/projects, api/git-repositories, api/workspaces, hooks/use-git-repo, types - workspace-create-form.tsx | React form component for creating workspaces with cascading project/repository/branch selectors and support for both contextual and standalone modes | exp: WorkspaceCreateFormProps, func:WorkspaceCreateForm({ onSubmit, onCancel, defaultProjectId, defaultRepoId, }: WorkspaceCreateFormProps), call:Boolean, call:useState, call:useGitRepo, call:useEffect, call:git.branches.includes, call:setSelectedBranch, call:setIsNewBranch, call:useCallback, call:listProjects, call:setProjects, call:setSelectedProject, call:setError, call:setFetchingProjects, call:loadProjects, call:setRepos, call:setSelectedRepo, call:listRepositories, call:loadRepos, call:setNewBranchName, call:e.preventDefault, call:name.trim, call:newBranchName.trim, call:setSubmitting, call:createWorkspaceTopLevel, call:onSubmit, call:projects.map, call:repos.map, call:setName, call:handleBranchChange, call:git.branches.map | dep: react, ../../icon, ../../../api/projects, ../../../api/git-repositories, ../../../api/workspaces, ../../../hooks/use-git-repo, ../../../types, icon, api/projects, api/git-repositories, api/workspaces, hooks/use-git-repo, types
- workspace-detail-header.tsx | Renders a header component for a workspace detail page showing breadcrumb navigation and branch information. | exp: WorkspaceDetailHeaderProps, func:WorkspaceDetailHeader({ workspace, }: WorkspaceDetailHeaderProps) | dep: ../../icon, react - workspace-detail-header.tsx | Renders a header component for a workspace detail page showing breadcrumb navigation and branch information. | exp: WorkspaceDetailHeaderProps, func:WorkspaceDetailHeader({ workspace, }: WorkspaceDetailHeaderProps) | dep: ../../icon, react
- workspace-file-panel.tsx | Renders a file browser and editor panel with Git integration for a workspace detail page. | exp: func:WorkspaceFilePanel({ workspaceId }: WorkspaceFilePanelProps), call:useWorkspaceFiles, call:useWorkspaceGit, call:useState, call:setSelectedPath, call:setIsEditing, call:setEditContent, call:navigateTo, call:loadFile, call:currentPath.split("/").slice(0, -1).join, call:saveFile, call:setCommitMessage, call:commit, call:entries.map, call:handleSelect | dep: react, ../../icon, ../../../hooks/use-workspace-files, ../../../hooks/use-workspace-git, ../../../api/workspace-files - workspace-file-panel.tsx | Renders a file browser and editor panel with Git integration for a workspace detail page. | exp: func:WorkspaceFilePanel({ workspaceId }: WorkspaceFilePanelProps), call:useWorkspaceFiles, call:useWorkspaceGit, call:useState, call:setSelectedPath, call:setIsEditing, call:setEditContent, call:navigateTo, call:loadFile, call:currentPath.split("/").slice(0, -1).join, call:saveFile, call:setCommitMessage, call:commit, call:entries.map, call:handleSelect | dep: react, ../../icon, ../../../hooks/use-workspace-files, ../../../hooks/use-workspace-git, ../../../api/workspace-files
@@ -17,7 +17,7 @@ Provides React UI components for workspace management, navigation, and detail vi
- workspace-tab-bar.tsx | Renders desktop and mobile tab bar components for workspace navigation with files, git, tools, and settings tabs. | exp: WorkspaceTab, func:WorkspaceTabBar({ active, onChange }: WorkspaceTabBarProps), call:TABS.map, call:onChange, func:WorkspaceMobileTabBar({ active, onChange, }: WorkspaceTabBarProps), call:TABS.map, call:onChange | dep: ../../icon, react - workspace-tab-bar.tsx | Renders desktop and mobile tab bar components for workspace navigation with files, git, tools, and settings tabs. | exp: WorkspaceTab, func:WorkspaceTabBar({ active, onChange }: WorkspaceTabBarProps), call:TABS.map, call:onChange, func:WorkspaceMobileTabBar({ active, onChange, }: WorkspaceTabBarProps), call:TABS.map, call:onChange | dep: ../../icon, react
- workspace-tools-panel.tsx | Renders a tools management panel for a workspace that displays running tool instances and allows starting new tools via a modal | exp: func:WorkspaceToolsPanel({ workspace }: WorkspaceToolsPanelProps), call:useWorkspaceInstances, call:useState, call:setShowModal, call:instances.map, call:e.stopPropagation, call:refresh | dep: react, ../../icon, ../tool/tool-starter, ../../../hooks/use-workspace-instances, ../../../types/workspace, icon, tool-starter, use-workspace-instances, workspace types - workspace-tools-panel.tsx | Renders a tools management panel for a workspace that displays running tool instances and allows starting new tools via a modal | exp: func:WorkspaceToolsPanel({ workspace }: WorkspaceToolsPanelProps), call:useWorkspaceInstances, call:useState, call:setShowModal, call:instances.map, call:e.stopPropagation, call:refresh | dep: react, ../../icon, ../tool/tool-starter, ../../../hooks/use-workspace-instances, ../../../types/workspace, icon, tool-starter, use-workspace-instances, workspace types
## arch ## arch
Feature-based component organization with page-specific composite components (header/detail/settings) and reusable atomic pieces (cards, chips, forms), following a panel/tab architecture for workspace detail layout. Feature-based component organization with compound page pattern (header/tab-bar/panels), form abstraction for creation, and modal-driven tool management with responsive mobile/desktop tab navigation.
## tags ## tags
workspace, call:set, call:use, panel, git, branch, react, call:on workspace, call:set, call:use, panel, git, branch, react, call:on
## symbols ## symbols
@@ -35,15 +35,18 @@ export function WorkspaceCard({
className="workspace-title-link" className="workspace-title-link"
> >
<div className="workspace-title-stack"> <div className="workspace-title-stack">
<div className="workspace-title-row">
<h4 className="workspace-name">{workspace.name}</h4>
<span className={`status-badge ${statusClass}`}>
{workspace.status}
</span>
</div>
<span className="workspace-project-name"> <span className="workspace-project-name">
<Icon name="projects" size="sm" />
{workspace.project_name} {workspace.project_name}
</span> </span>
<h4 className="workspace-name">{workspace.name}</h4>
</div> </div>
</Link> </Link>
<span className={`status-badge ${statusClass}`}>
{workspace.status}
</span>
</div> </div>
<div className="workspace-card-body"> <div className="workspace-card-body">
+3 -1
View File
@@ -182,7 +182,9 @@ export const ProjectsPage = () => {
)} )}
<div className="mobile-list-item-chips"> <div className="mobile-list-item-chips">
{(project.repositories?.length ?? 0) === 0 ? ( {(project.repositories?.length ?? 0) === 0 ? (
<span className="mobile-list-item-chip muted">No repositories</span> <span className="mobile-list-item-chip muted">
No repositories
</span>
) : ( ) : (
project.repositories.map((repo) => ( project.repositories.map((repo) => (
<span key={repo.id} className="mobile-list-item-chip"> <span key={repo.id} className="mobile-list-item-chip">
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/web/src/styles dir: apps/web/src/styles
## role ## role
Houses the complete CSS styling system for a web application, including design tokens, global styles, syntax highlighting themes, and utility classes. Provides the complete visual design system and styling foundation for the web application, encompassing global styles, design tokens, utility classes, and component-specific styles.
## parent ## parent
index: apps/web/src/.pi-map.index.md index: apps/web/src/.pi-map.index.md
map: apps/web/src/.pi-map.md map: apps/web/src/.pi-map.md
+4 -4
View File
@@ -4,16 +4,16 @@ dir: apps/web/src/styles
index: apps/web/src/styles/.pi-map.index.md index: apps/web/src/styles/.pi-map.index.md
## role ## role
Houses the complete CSS styling system for a web application, including design tokens, global styles, syntax highlighting themes, and utility classes. Provides the complete visual design system and styling foundation for the web application, encompassing global styles, design tokens, utility classes, and component-specific styles.
## files ## files
- global.css | Defines global CSS styles for a web application shell layout, navigation, cards, forms, dialogs, settings pages, and responsive design patterns. | dep: CSS custom properties (CSS variables: --border, --panel, --brand, --muted, --ink, --bg, --danger, --success, --warning, --font-size-xs, --font-size-sm, --space-2, --space-3, --space-4, --space-5) - global.css | Defines global CSS styles for a web application shell layout, navigation, cards, forms, dialogs, settings pages, and responsive design patterns. | dep: CSS custom properties (CSS variables: --border, --panel, --brand, --muted, --ink, --bg, --danger, --success, --warning, --font-size-xs, --font-size-sm, --space-2, --space-3, --space-4, --space-5)
- syntax-highlight.css | Stylesheet for a syntax highlighting component with toolbar, line numbers, code display, and Prism.js theme integration | dep: Prism.js - syntax-highlight.css | Stylesheet for a syntax highlighting component with toolbar, line numbers, code display, and Prism.js theme integration | dep: Prism.js
- tokens.css | Defines a comprehensive CSS design token system with light/dark themes, spacing scales, breakpoints, and fluid typography for a web application. - tokens.css | Defines a comprehensive CSS design token system with light/dark themes, spacing scales, breakpoints, and fluid typography for a web application.
- utilities.css | Provides a comprehensive responsive CSS utility system with terminal-specific styling, mobile adaptations, and component patterns for a web application. | dep: CSS custom properties (var(--space-*), var(--border), var(--bg), var(--text), var(--brand), var(--muted), var(--success), var(--danger), var(--danger-light)), xterm.js (terminal integration) - utilities.css | Provides responsive CSS utility classes and component-specific styles for a web application featuring terminals, dialogs, tables, forms, and navigation with mobile-first breakpoints | dep: CSS custom properties (variables like --space-*, --border, --bg, --brand, --muted, --success, --danger, --text-xs, --text-sm), xterm.js (terminal library)
## arch ## arch
Token-driven CSS architecture using CSS custom properties for themeable light/dark modes, utility-first responsive patterns, and component-scoped stylesheets with Prism.js integration for syntax highlighting. CSS custom properties (variables) based theming system with light/dark mode support, mobile-first responsive breakpoints, fluid typography scales, utility-first class patterns, and modular separation of concerns across tokens, utilities, global layouts, and component-specific styles.
## tags ## tags
var(, space, global, css, web, application, syntax, defines space, global, css, web, application, syntax, defines, styles
## symbols ## symbols
- -
## workflows ## workflows
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/web/src/styles/pages dir: apps/web/src/styles/pages
## role ## role
Package containing page-specific CSS stylesheets for individual web application views Contains page-specific CSS stylesheets for individual application views in the web frontend, providing scoped styling for distinct feature areas.
## parent ## parent
index: apps/web/src/styles/.pi-map.index.md index: apps/web/src/styles/.pi-map.index.md
map: apps/web/src/styles/.pi-map.md map: apps/web/src/styles/.pi-map.md
+4 -4
View File
@@ -4,18 +4,18 @@ dir: apps/web/src/styles/pages
index: apps/web/src/styles/pages/.pi-map.index.md index: apps/web/src/styles/pages/.pi-map.index.md
## role ## role
Package containing page-specific CSS stylesheets for individual web application views Contains page-specific CSS stylesheets for individual application views in the web frontend, providing scoped styling for distinct feature areas.
## files ## files
- git-history.css | Stylesheet for a Git history visualization page with commit list, branch selector, and detail panel layout | dep: CSS custom properties (var(--border), var(--panel), var(--ink), var(--brand), var(--muted)) - git-history.css | Stylesheet for a Git history visualization page with commit list, branch selector, and detail panel layout | dep: CSS custom properties (var(--border), var(--panel), var(--ink), var(--brand), var(--muted))
- projects.css | Stylesheet for a projects page UI featuring expandable project toggles, repository listings, workspace grids, and repository creation radio controls. | dep: CSS custom properties (variables) - projects.css | Stylesheet for a projects page UI featuring expandable project toggles, repository listings, workspace grids, and repository creation radio controls. | dep: CSS custom properties (variables)
- sessions.css | Stylesheet for session management UI components including cards, dropdowns, forms, and responsive mobile layouts. | dep: CSS custom properties/variables (var(--border), var(--panel), var(--text), var(--space-*), var(--danger), var(--color-border)) - sessions.css | Stylesheet for session management UI components including cards, dropdowns, forms, and responsive mobile layouts. | dep: CSS custom properties/variables (var(--border), var(--panel), var(--text), var(--space-*), var(--danger), var(--color-border))
- ssh-keys.css | Styles a responsive SSH key list component that stacks vertically on mobile and switches to horizontal layout on larger screens | dep: CSS custom properties (var(--space-3), var(--space-2), var(--space-4), var(--bg), var(--border)) - ssh-keys.css | Styles a responsive SSH key list component that stacks vertically on mobile and switches to horizontal layout on larger screens | dep: CSS custom properties (var(--space-3), var(--space-2), var(--space-4), var(--bg), var(--border))
- workspace-detail.css | Stylesheet for a workspace detail page with tabbed interface including files, git, tools, and settings views, plus a workspace creation form | dep: CSS custom properties (variables for colors, spacing, typography) - workspace-detail.css | Stylesheet for a workspace detail page with tabbed interface including files, git, tools, and settings views, plus a workspace creation form | dep: CSS custom properties (variables for colors, spacing, typography)
- workspaces.css | Styles a workspaces page with responsive card grid layout, status badges, and instance chips | dep: CSS custom properties (var(--space-*), var(--border), var(--brand), var(--panel), var(--ink), var(--muted), var(--bg), var(--success), var(--warning), var(--danger), var(--font-size-*)) - workspaces.css | Stylesheet for a workspaces management page featuring responsive grid layout, workspace cards with status badges, instance chips, and mobile adaptations. | dep: CSS custom properties (design tokens: --space-*, --border, --panel, --brand, --ink, --muted, --bg, --success, --warning, --danger, --font-size-*)
## arch ## arch
Modular CSS architecture with dedicated stylesheets per page domain, featuring responsive design patterns, component-based styling, and mobile-first breakpoint strategies Modular CSS architecture with page-level style separation, responsive design patterns using media queries for mobile adaptation, and component-scoped styling organized by feature domain rather than shared component library.
## tags ## tags
var(, workspace, space, stylesheet, git, page, detail, history var(, workspace, stylesheet, space, git, page, detail, history
## symbols ## symbols
- -
## workflows ## workflows
+23 -10
View File
@@ -40,7 +40,6 @@
.workspace-card-top { .workspace-card-top {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: space-between;
gap: var(--space-3); gap: var(--space-3);
} }
@@ -63,15 +62,11 @@
min-width: 0; min-width: 0;
} }
.workspace-project-name { .workspace-title-row {
font-size: var(--font-size-sm); display: flex;
font-weight: 500; align-items: center;
color: var(--muted); gap: var(--space-2);
text-transform: uppercase; min-width: 0;
letter-spacing: 0.04em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.workspace-name { .workspace-name {
@@ -82,6 +77,24 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
flex: 1;
min-width: 0;
}
.workspace-project-name {
display: inline-flex;
align-items: center;
gap: var(--space-1);
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.workspace-project-name .icon {
color: var(--brand);
} }
/* Body: meta row + instance chips */ /* Body: meta row + instance chips */
+33 -4
View File
@@ -553,6 +553,12 @@ a.nav-item,
overflow: hidden; overflow: hidden;
} }
/* Let xterm.js's own viewport handle touch; we only lock the outer page */
.terminal-container .xterm-viewport {
touch-action: auto;
overscroll-behavior: auto;
}
/* xterm.js manages its own positioning and sizing */ /* xterm.js manages its own positioning and sizing */
.terminal-container canvas { .terminal-container canvas {
@@ -566,6 +572,11 @@ a.nav-item,
padding: 0; padding: 0;
} }
.terminal-wrapper.mobile {
touch-action: none;
overscroll-behavior: none;
}
/* Ensure xterm viewport fills container properly */ /* Ensure xterm viewport fills container properly */
/* Responsive terminal */ /* Responsive terminal */
@media (max-width: 767px) { @media (max-width: 767px) {
@@ -855,17 +866,23 @@ a.nav-item,
Mobile Terminal Overlay Mobile Terminal Overlay
============================================ */ ============================================ */
/* Mobile terminal page — no padding, terminal fills viewport */ /* Mobile terminal page — no padding, terminal fills viewport, locked in place */
.terminal-page.mobile { .terminal-page.mobile {
padding: 0; padding: 0;
gap: 0; gap: 0;
height: 100vh; height: 100vh;
height: 100dvh; height: 100dvh;
position: relative; position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden; overflow: hidden;
touch-action: none;
overscroll-behavior: none;
} }
/* Overlay status bar — floats over terminal, never resizes it */ /* Overlay status bar — floats over terminal; only its buttons consume pointer events */
.mobile-terminal-overlay { .mobile-terminal-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -874,11 +891,21 @@ a.nav-item,
z-index: 100; z-index: 100;
background: #2d2d2d; background: #2d2d2d;
border-bottom: 1px solid #3e3e3e; border-bottom: 1px solid #3e3e3e;
pointer-events: none;
transition: transition:
transform 0.3s ease, transform 0.3s ease,
opacity 0.3s ease; opacity 0.3s ease;
} }
.mobile-terminal-overlay.visible {
pointer-events: auto;
}
.mobile-terminal-overlay > *,
.mobile-terminal-overlay-tabs {
pointer-events: auto;
}
.mobile-terminal-overlay.hidden { .mobile-terminal-overlay.hidden {
transform: translateY(-100%); transform: translateY(-100%);
opacity: 0; opacity: 0;
@@ -978,13 +1005,15 @@ a.nav-item,
border-bottom: none; border-bottom: none;
} }
/* Terminal content — always fills full viewport on mobile */ /* Terminal content — always fills full viewport on mobile, locked against page scroll */
.terminal-page-content.mobile-full { .terminal-page-content.mobile-full {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
border: none; border: none;
border-radius: 0; border-radius: 0;
overflow: hidden; overflow: hidden;
touch-action: none;
overscroll-behavior: none;
} }
/* Mobile fullscreen */ /* Mobile fullscreen */