diff --git a/NOTICE b/NOTICE index 52cb373e..b298cde6 100644 --- a/NOTICE +++ b/NOTICE @@ -27,6 +27,33 @@ CC0 1.0 Universal; they are used without changes. The marks belong to their owners; contr uses them only to show which service a tool call goes to, or which model family a worker runs on. +The pull request and issue icons in assets/icons/github/ are Octicons +(https://github.com/primer/octicons), v19.21.1, licensed under the MIT +License; they are used without changes. contr draws them in its own ink +colors to show the state of a pull request or an issue. + + MIT License + + Copyright (c) 2025 GitHub Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + The Claude Code mascot (assets/icons/claude-mascot.svg) is the "Claude Code" icon of LobeHub icons (https://github.com/lobehub/lobe-icons), licensed under the MIT License; changes: the viewBox is cropped to the shape, and it has one diff --git a/assets/icons.go b/assets/icons.go index 874543f5..5339714d 100644 --- a/assets/icons.go +++ b/assets/icons.go @@ -9,7 +9,7 @@ import "embed" // `icons/.svg`, and the brand marks of servers (Simple Icons, CC0) as // `icons/brands/.svg`. // -//go:embed icons/*.svg icons/brands/*.svg +//go:embed icons/*.svg icons/brands/*.svg icons/github/*.svg var Icons embed.FS // Fonts holds the faces of JetBrains Mono and Inter as `fonts/.ttf`. diff --git a/assets/icons/github/git-merge.svg b/assets/icons/github/git-merge.svg new file mode 100644 index 00000000..adeaca22 --- /dev/null +++ b/assets/icons/github/git-merge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/github/git-pull-request-closed.svg b/assets/icons/github/git-pull-request-closed.svg new file mode 100644 index 00000000..18db3978 --- /dev/null +++ b/assets/icons/github/git-pull-request-closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/github/git-pull-request-draft.svg b/assets/icons/github/git-pull-request-draft.svg new file mode 100644 index 00000000..efbec9a6 --- /dev/null +++ b/assets/icons/github/git-pull-request-draft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/github/git-pull-request.svg b/assets/icons/github/git-pull-request.svg new file mode 100644 index 00000000..a1cb1ef9 --- /dev/null +++ b/assets/icons/github/git-pull-request.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/github/issue-closed.svg b/assets/icons/github/issue-closed.svg new file mode 100644 index 00000000..befad602 --- /dev/null +++ b/assets/icons/github/issue-closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/github/issue-opened.svg b/assets/icons/github/issue-opened.svg new file mode 100644 index 00000000..914e0869 --- /dev/null +++ b/assets/icons/github/issue-opened.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 72667d4e..5cca7621 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -259,3 +259,4 @@ replaces it. | D282 | 2026-11-11 | A Claude worker gets the PATH of the process that spawns it (#728). `hc update` restarts the core under launchd, whose PATH is short, and each terminal inherits the PATH of the core, so `claude` was not found and the worker ended at once. Spawn now sets `CONTR_PATH` in the terminal: the PATH of the spawning process, then `~/.local/bin`, `/opt/homebrew/bin` and `/usr/local/bin`, without doubles. The clean wrapper (D216) adds these folders after the PATH of the core and removes the variable, so the PATH of the core still comes first. A program that does not start through the clean wrapper (not Claude) gets no change. | Seen on 2026-11-11 after the first `Update hc` (D277): two spawns ended with "claude: not found". | | D283 | 2026-11-11 | The contr plugin row of Settings → Setup gets one action (#713, S3; owner decision D65). `Install` when the plugin is not installed, `Enable` when it is disabled. A click opens a confirm with the exact command, the same text as the copy line; only `Run` runs it, off the UI thread, with a 2 min limit, and only these two lines with the `claude` the checks find. A spinner shows while it runs; a failure shows the last stderr line under the row; a success runs a recheck. The copy line stays. No other row gets an action (meter D66, gh and hc copy only); contr starts no hc core here and edits no file in Claude Code's folders. | Before, the owner had to copy the line to a terminal. The confirm keeps the run explicit. | | D285 | 2026-11-11 | In the chat, a worker shows the mark of its model family in place of its 6px dot (#714, slice A): the Claude Code mascot, the OpenAI mark (Codex) or the DeepSeek mark, 12px, in the note lines and the work group's fold row. The family comes from what contr already knows of the worker: its model label, else the model of its profile, else its program (`claude`, `codex`); no new read. A worker of another program keeps the dot. The state reads as the dot's did: the mark takes the dot's ink (blocked `ink-100`, working `ink-88`, else `ink-30`) and hops as the working Claude mascot does while the worker works (still with Reduce Motion). No hue: blocked stays grey in the chat (D259, D261), and the mascot is grey too, as `claude` is the orchestrator's. Only the chat changes; the sidebar and the tabs keep their glyphs. The marks are from Simple Icons (CC0). | +| D286 | 2026-11-11 | PR and issue chips show GitHub's Octicons (#714, slice B; docs/spec/ChatView.md, Chips and Blocks). A PR shows the pull request icon of its state: open, draft, merged or closed; an issue the issue icon, open or closed. The icons are in `assets/icons/github` (Octicons, MIT, in NOTICE) and `icons.PRIcon`, `icons.IssueIcon` map gh's states to them. They take an ink token (`ink-48`), never GitHub's colors (D119, D261). The dot of the checks (running, passed, failed) stays after the icon, so the chip still shows CI; the open and closed dots go, as the icon says it. The read of a number (`gh pr view`, D262) adds `isDraft`; no other gh read is added. The board's PR cell shows the same icon. The work rows of the board panel (hidden, D147) are not changed. | The owner asked for GitHub's icons (#714). | diff --git a/docs/spec/ChatView.md b/docs/spec/ChatView.md index 56a9fdea..59a3710b 100644 --- a/docs/spec/ChatView.md +++ b/docs/spec/ChatView.md @@ -267,14 +267,15 @@ does not know stays plain text. | Chip | Recognized | Known when | Dot | Click | |---|---|---|---|---| -| PR | "#667" | gh says it is a pull request of the session's repository (read by number), or it is the PR of a worker's branch (D104, D145) | open `content-info`, checks running `content-running`, passed `content-done`, failed `content-failed`, merged `content-done`, closed `content-info`. A failed check wins over a pending one, a pending one over a passed one. | opens the PR as the agent rows do: in the browser (the PR pane is hidden, D147) | -| Issue | "#649" | gh says it is an issue | none | opens the issue in the browser | +| PR | "#667" | gh says it is a pull request of the session's repository (read by number), or it is the PR of a worker's branch (D104, D145) | GitHub's pull request Octicon of its state, 0.9 of the text size, `ink-48`: open, draft, merged, closed (D286). An open PR with checks adds a 6px dot after it: running `content-running`, passed `content-done`, failed `content-failed`. A failed check wins over a pending one, a pending one over a passed one. | opens the PR as the agent rows do: in the browser (the PR pane is hidden, D147) | +| Issue | "#649" | gh says it is an issue | GitHub's issue Octicon, open or closed, 0.9 of the text size, `ink-48` (D286) | opens the issue in the browser | | Worker | a word that is the name of a worker contr lists ("b-649"); a live one wins | always | needs you `content-failed`, working `content-running`, done (ended) `content-done`, waiting (idle) `content-waiting` | selects the worker in the sidebar | | File | a word with a slash or an extension, with an optional ":42" | it is a file in the session's folder | none: the `file` line icon, 0.9 of the text size, `ink-48` | opens the file in the Review view, as the files panel does (the line is not used) | - The dot is 6px, round, `space-4` before the text, its center near the - middle of the lowercase letters. The tip of a chip says what it is: - "PR #667 · checks passed", "Worker b-649 · needs you", "Open + middle of the lowercase letters. The Octicons take the ink of the + text around them, never GitHub's colors (D119, D261, D286). The tip of a + chip says what it is: "PR #667 · checks passed", "Draft PR #661", "Worker b-649 · needs you", "Open internal/app/x.go". - A number is read only while its text is on screen and only once: the answer holds for 5 minutes, 1 minute for a PR with checks running, and @@ -284,8 +285,8 @@ does not know stays plain text. - A chip shows the state of now: it follows the agent state and the answers of the reads. Nothing polls and no timer runs (D55); a text at rest asks for no frame. -- Not yet: the quiet fill behind a chip, the mono text, the PR and issue - icons and the "merged" word of the mockup (K2, KC1). The chip is a link +- Not yet: the quiet fill behind a chip, the mono text and the "merged" + word of the mockup (K2, KC1). The chip is a link of the markdown view, which styles all its links alike. ## Blocks @@ -294,7 +295,7 @@ A coordinator writes a status board or a merge queue as a fenced block (D267). T | Block | Syntax | Shows | |---|---|---| -| Status board | info string `contr-status`; a line ` \| # \| ` | six columns: Worker, Issue, Builds, State, PR, Checks; heads mono `meta`, `ink-48`; no box, a `hairline` above and below the grid, `space-16` between columns; the worker in `body`, `ink-88`, after its 6px dot (the chip's worker colors); the issue mono `meta` `ink-64`; what it builds `body` `ink-88`, one line with "…"; the state in mono `meta`, `ink-64` (grey: only the dots have content colors), "needs you" in `blocked-text`; the PR as a mono 12px number with its dot on `hover`, radius 5, a click opens it as a chip does; the checks "2 of 3" (passed of passed, failed and pending) mono `meta` `ink-64`; "—" for none | +| Status board | info string `contr-status`; a line ` \| # \| ` | six columns: Worker, Issue, Builds, State, PR, Checks; heads mono `meta`, `ink-48`; no box, a `hairline` above and below the grid, `space-16` between columns; the worker in `body`, `ink-88`, after its 6px dot (the chip's worker colors); the issue mono `meta` `ink-64`; what it builds `body` `ink-88`, one line with "…"; the state in mono `meta`, `ink-64` (grey: only the dots have content colors), "needs you" in `blocked-text`; the PR as its Octicon (12px, `ink-48`, D286), the dot of its checks when it has some, and a mono 12px number on `hover`, radius 5, a click opens it as a chip does; the checks "2 of 3" (passed of passed, failed and pending) mono `meta` `ink-64`; "—" for none | | Merge queue | info string `contr-queue`; a line `#