diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf4825..456beee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Disk write amplification fix for long-running PTY-heavy sessions. - **Hot-path redesign for heavy TUI workloads** — `pumpLogs` no longer acquires `stateMu` per 1024-byte PTY chunk; the ring buffer pointer is pre-fetched once and the per-chunk lookup is a single `atomic.Pointer.Load`. The ring buffer's backing slice is allocated eagerly so the first Write never blocks on a 32 MB malloc. A new `WriteString` path avoids the `[]byte(chunk)` conversion that would otherwise happen on every chunk. - **Daemon resource monitoring** — the resource stats API (`GET /api/instances/stats`) now includes the mw daemon process itself in global totals (`daemon_cpu_percent`, `daemon_memory_bytes`). The UI displays a dedicated "mw daemon" row so users can distinguish daemon overhead from instance resource usage. - **Ring buffer usage reporting** — per-instance stats now expose `memory_buffer_bytes` (actual usage) and `memory_buffer_cap_bytes` (pre-allocated capacity). The UI memory column shows the combined `RSS + buffer_used` with a `buf used/cap` annotation for active buffers, giving users visibility into per-instance buffer memory cost. +- **Sidebar GitHub link** — main workspace row now shows a small GitHub Mark icon to the right of the project name when `git remote` resolves to `github.com`; clicking opens the canonical `https://github.com//` URL in a new tab. Source of truth is a new `github_url` field on `GET /api/main`, computed via the new `gitx.GitHubURL` helper (prefers `origin`, then falls back to iterating `git remote`; normalizes SCP / HTTPS / `ssh://` forms; strips `.git`). GitHub Enterprise and non-GitHub remotes are intentionally not surfaced. ## v0.3.0 diff --git a/README.md b/README.md index 145cce9..24bd9de 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ myworktree is a thin management layer that: - MCP tool endpoints (`/api/mcp/tools`, `/api/mcp/call`) - Portal Dashboard with shared entry port, auto-discovery of running instances across repos - Global auth token (HttpOnly Cookie, CSRF protection, tailscale serve integration) +- Sidebar main workspace shows a GitHub icon next to the project name when the repo's git remote points at `github.com`; clicking opens the canonical `https://github.com//` URL in a new tab. GitHub Enterprise and non-GitHub remotes are intentionally not surfaced. ## Requirements - macOS 12+ (other platforms are not validated yet) diff --git a/README.zh-CN.md b/README.zh-CN.md index ffe12e4..908b045 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -37,6 +37,7 @@ myworktree 只做管理,不碰项目具体内容: - MCP 接口(`/api/mcp/tools`、`/api/mcp/call`) - Portal 仪表板:共享入口端口,跨仓库自动发现运行实例 - 全局认证 Token(HttpOnly Cookie、CSRF 防护、Tailscale Serve 自动集成) +- 侧栏主工作区项目名旁在仓库 remote 指向 `github.com` 时渲染 GitHub 图标;点击在新窗口打开 `https://github.com//` 规范 URL。GitHub Enterprise 与非 GitHub remote 不展示。 ## 运行环境 - macOS 12+ 其他平台未验证 diff --git a/docs/API.md b/docs/API.md index 31e0414..b72b295 100644 --- a/docs/API.md +++ b/docs/API.md @@ -31,11 +31,21 @@ Returns the main (host) git repository name and its currently checked-out branch Response: ```json -{ "name": "myproject", "branch": "feature/ui-update" } +{ + "name": "myproject", + "branch": "feature/ui-update", + "github_url": "https://github.com/owner/myproject" +} ``` -- `name`: basename of the git root directory +- `name`: basename of the git root directory. - `branch`: currently checked-out branch (via `git rev-parse --abbrev-ref HEAD`). Returns empty string on detached HEAD (e.g., CI shallow clones). +- `github_url`: when the main repo has a git remote pointing at `github.com`, returns the canonical `https://github.com//` URL; otherwise returns an empty string. Resolution order: + 1. `git remote get-url origin` (preferred). + 2. If `origin` is missing or not parseable, fall back to iterating `git remote` and trying each remote in declared order. + 3. Supported URL formats: `@github.com:owner/repo.git` (SCP-style; the user segment is arbitrary — `git` is the conventional default, but `~/.ssh/config` aliases and CI bots commonly use other usernames), `https://github.com/owner/repo.git`, `ssh://[user@]github.com/owner/repo.git` (no explicit port — `ssh://git@github.com:22/...` is **not** recognized). The `.git` suffix and a trailing `/` are stripped. + 4. Only host `github.com` (case-insensitive) is recognized. GitHub Enterprise (`*.ghe.com`, self-hosted) and any non-GitHub host (GitLab, Bitbucket, local paths, `file://`) yield an empty string. + 5. The field is always present in the JSON response (an empty string means "no GitHub link to show"). ## 2) Worktrees ### List diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 475c461..bcb5b32 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -61,7 +61,8 @@ It does **not** analyze project code or prevent concurrent write conflicts insid The sidebar shows a pinned **Main Workspace** item at the top (purple accent), followed by a "Worktrees" divider and the managed worktree list. -- **Main repo**: `GET /api/main` returns `{name, branch}`. The branch is live — queried via `git rev-parse --abbrev-ref HEAD` (via `gitx.CurrentBranch`). Returns empty string for `branch` on detached HEAD (e.g., CI shallow clones), otherwise returns the current branch name. +- **Main repo**: `GET /api/main` returns `{name, branch, github_url}`. The branch is live — queried via `git rev-parse --abbrev-ref HEAD` (via `gitx.CurrentBranch`). Returns empty string for `branch` on detached HEAD (e.g., CI shallow clones), otherwise returns the current branch name. +- **GitHub link on the main repo row**: When `state.mainRepo.github_url` is non-empty, the sidebar main-workspace row renders a small GitHub Mark icon to the right of the project name. The icon is an `` (so middle-click / right-click "Open in new tab" still work), with `event.stopPropagation()` so clicking the icon does not trigger the row's `selectWorktree` handler. The URL is resolved on every `/api/main` request via `gitx.GitHubURL(root)`: it inspects `origin` first, then falls back to iterating `git remote`, normalizes SCP / HTTPS / `ssh://` forms, and returns `""` for any host other than `github.com` (case-insensitive) — GitHub Enterprise and non-Git remotes intentionally do not surface a link. When the field is empty, the icon is omitted entirely (the project name occupies the full row width). - **Worktrees**: `GET /api/worktrees` also returns live branches — `worktree.Manager.List()` queries `git rev-parse --abbrev-ref HEAD` per worktree path on each call. The `branch` field reflects the currently checked-out branch, not the creation-time branch. - **Quick actions**: The main repo item and each managed worktree row render two SVG shortcut buttons when the UI is accessed via `localhost` or `127.0.0.1`: one opens Terminal at the target path, the other opens Finder. These buttons are intentionally hidden for remote/browser sessions because the action targets the host machine running `myworktree`, not the remote client device. - **Server-side boundary**: The backend does not trust the frontend visibility check. `POST /api/worktrees/open-terminal` and `POST /api/worktrees/open-finder` reject non-loopback clients based on the request's remote address, so remote callers cannot trigger host GUI actions by directly invoking the API. diff --git a/docs/GHOSTTY_WEB_RESEARCH.md b/docs/GHOSTTY_WEB_RESEARCH.md new file mode 100644 index 0000000..0bb9e8f --- /dev/null +++ b/docs/GHOSTTY_WEB_RESEARCH.md @@ -0,0 +1,139 @@ +# ghostty-web 调研报告:与 myworktree 的对比与借鉴 + +**调研日期**: 2026-06-19 +**目标项目**: https://github.com/anomalyco/ghostty-web +**调研目的**: 评估 ghostty-web 是否可作为 myworktree 前端终端渲染器(xterm.js)的替代方案 +**结论摘要**: ghostty-web 精准命中 myworktree 已记录的两类 xterm.js 痛点(OSC/DA 查询回声、复杂脚本渲染),推荐作为可选 renderer 引入,feature flag 灰度过渡。 + +--- + +## 1. 项目背景 + +**ghostty-web** 是 Coder 公司 fork 自 `coder/ghostty-web` 的一个 TypeScript 库,把原生 [Ghostty](https://github.com/ghostty-org/ghostty) 终端模拟器的解析层编译成 WASM,在浏览器里跑出 xterm.js 兼容的 `Terminal` API。 + +**核心卖点**(来自上游 README): +- 与 xterm.js 完全 API 兼容:「Migrate from xterm by changing your import: `@xterm/xterm` → `ghostty-web`」 +- WASM 编译的 VT 解析器,与原生 Ghostty 共用同一份经过实战检验的代码 +- 零运行时依赖,~400KB WASM bundle +- 修掉 xterm.js 的两个长期 issue: + - 复杂脚本(Devanagari、Arabic)渲染 + - XTPUSHSGR/XTPOPSGR 等控制序列缺失 +- 依赖 Mitchell Hashimoto 正在做的 [libghostty](https://mitchellh.com/writing/libghostty-is-coming),目前 patch 极小(见 `patches/ghostty-wasm-api.patch`) + +**目录结构**: +``` +ghostty-web/ +├── lib/ # TS 库主体(Terminal、Buffer、Renderer、InputHandler、SelectionManager、LinkDetector…) +├── demo/ # Web demo,连接真实 shell +├── dist/ # 构建产物 +├── ghostty/ # 子模块:upstream Ghostty 源码 +├── patches/ # Ghostty 的 WASM 补丁 +├── scripts/ # 构建脚本 +├── bench/ # 基准测试 +├── AGENTS.md # AI 协作指南 +├── biome.json # Biome 格式化 + lint 配置 +├── bun.lock # Bun 依赖锁 +├── vite.config.js # Vite 构建 +└── flake.nix # Nix 开发环境 +``` + +--- + +## 2. 与 myworktree 的相同点 + +两者都面向「**浏览器里的终端体验**」,且都把一个终端渲染器 vendored 到前端用: + +| 维度 | ghostty-web | myworktree | +|---|---|---| +| 终端渲染器 | `ghostty-vt.wasm`(Zig→WASM 的 Ghostty VT 解析器) | `xterm.js v6.0.0` + `xterm-addon-fit`(vendor 在 `internal/ui/static/vendor/`) | +| 接入方式 | 前端 JS 库 + WASM | 前端 `