chore: v2.8.7 — Git SSH keys in tabs, safer Artifacts preview and search

- Settings → Git → Your git identity: SSH keys are one tab per host kind
  (GitHub · GitLab, AWS CodeCommit, More keys, Other hosts); opens on the
  AWS tab when only an RSA key exists.
- Artifacts: a file type the sandboxed preview cannot show (Office, zip,
  PDF) offers Download only, never a blank Preview.
- Artifacts search: `%`/`_`/`\` in a query match literally (LIKE ... ESCAPE).
- Skill frontmatter rewrite replaces a key with its continuation lines and
  quotes values YAML could misread.
- Plugin marketplace.json is read with a 1 MB streaming cap.
- Artifact lists look each owner up once.
This commit is contained in:
2026-10-15 17:17:47 +07:00
parent 4d6067650a
commit a58e95a957
16 changed files with 763 additions and 380 deletions
+26 -1
View File
@@ -6,7 +6,9 @@
* a plugin is picked from the list its marketplace link answers (a link the
* server cannot read opens the by-hand form), a skill is uploaded from one
* Markdown file under the name the user types, or written in the dialog.
* A card's right-click menu runs the same actions (Preview, Delete).
* A card's right-click menu runs the same actions (Preview, Delete). A file
* the sandbox cannot show (Office, zip, PDF) offers Download only, and the
* Add menu closes on Escape or a click outside it.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
@@ -253,4 +255,27 @@ describe("Artifacts", () => {
"/api/artifacts/1/raw?path=report.html"
);
});
it("offers no preview for a file the sandbox cannot show, only its download", async () => {
vi.mocked(api.artifacts.list).mockResolvedValue({
items: [item({ title: "plan.docx", files: [{ path: "plan.docx", size: 10 }] })],
});
open();
const card = await screen.findByTestId("artifact-1");
expect(within(card).queryByRole("button", { name: "Preview" })).toBeNull();
expect(within(card).getByRole("link", { name: "Download" })).toBeInTheDocument();
});
it("closes the Add menu on Escape and on a click outside it", async () => {
const user = userEvent.setup();
open();
await screen.findByText("deploy");
await user.click(screen.getByTestId("artifacts-add"));
expect(screen.getByRole("button", { name: "Skill" })).toBeInTheDocument();
await user.keyboard("{Escape}");
expect(screen.queryByRole("button", { name: "Skill" })).toBeNull();
await user.click(screen.getByTestId("artifacts-add"));
await user.click(screen.getByText("report.html"));
expect(screen.queryByRole("button", { name: "Skill" })).toBeNull();
});
});
@@ -7178,7 +7178,7 @@ exports[`screen snapshots > Settings — about 1`] = `
<span
class="text-status-warning/90"
>
UI build v2.8.6 (rebuild client to match)
UI build v2.8.7 (rebuild client to match)
</span>
</p>
</div>