feat: v2.7.8 — Artifacts hub, clone related repos as extra folders, quieter resume prompt

- Artifacts (/artifacts): claude.ai pages sessions publish (captured from the
  Artifact tool's hook), pinned workspace files, skills and plugins, each
  kept as a snapshot; shared ones appear in a Hub and a skill or plugin
  installs into the viewer's own account (server/lib/artifacts.js,
  /api/artifacts, docs/plans/artifacts/).
- Workspace extra folders: clone one or more related repos beside the
  workspace and add them in one step (POST /api/lanes/:id/sources/clone).
- A pane that ended by itself while tmux kept running is no longer offered
  for resume (tmux.serverStartedAt in reconcileOrphans).
- Changes panel: a folder that is not a git repo opens on Files, and its
  files open from there.
- Plus the parallel sessions' work in this tree: File Explorer button in the
  panel header, user delete, RSA SSH keys, chat/terminal and pet fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 16:04:03 +07:00
parent e94038481e
commit 22de5009ff
79 changed files with 4617 additions and 231 deletions
@@ -4,7 +4,8 @@
* /api/lanes/:id/sources…`, see server/lib/lane-sources.js). Shown as the
* workspace home's right column and, `compact`, folded at the bottom of the
* session view's Changes panel. Folder suggestions are the repo's untracked
* worktrees from `GET /api/projects`, filtered by Settings → Worktrees.
* worktrees from `GET /api/projects`, filtered by Settings → Worktrees; the
* same form clones related repos beside the workspace (`CloneSourcesForm`).
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
@@ -21,6 +22,7 @@ import { useProjectTreePrefs } from "../../lib/projectTreePrefs";
import { MarkdownPreview } from "../MarkdownPreview";
import { ContextMenu, MenuItem, MenuSeparator, useContextMenu } from "../ContextMenu";
import { PathPickButton } from "../PathPickButton";
import { CloneSourcesForm } from "./CloneSourcesForm";
function fmtBytes(n: number | null): string {
if (!n) return "";
@@ -436,6 +438,16 @@ function ContextBody({
)}
</form>
)}
{folderOpen && (
<CloneSourcesForm
laneId={lane.id}
parent={lane.cwd.replace(/\/+$/, "").replace(/\/[^/]*$/, "") || "/"}
onAdded={(next) => {
setView(next);
onSourcesChange?.(next);
}}
/>
)}
</section>
</aside>