7a88b74016
- Artifacts hub: upload a file from the computer; make a skill by uploading a Markdown file, files, a folder or a .zip/.skill, or by writing it in the dialog; add plugins by picking from the list a marketplace link answers (POST /api/artifacts/plugin/inspect); Settings → AI tools → Artifacts Hub. - Console tabs are combos of up to four split panes (ConsoleTabs, PaneGrid). - Accounts: an own avatar (PUT/DELETE /api/me/avatar, raster only, ≤ 1 MB, <data dir>/avatars/), shown with name and role at the sidebar's foot and on the workspace owner groups (GET /api/users/:id/avatar, owner_avatar_at). - Users page: per-user storage (GET /api/users/storage, measured in a worker) and account-pool spend/tokens per account (server/lib/pool-usage.js). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
/**
|
|
* @file PaneGrid.test.ts
|
|
* @description The pane drop preview: it draws where the dragged pane will
|
|
* actually land (`dropPreview` over `applyDrop`), not half of the target's
|
|
* current box, and nothing for a drop that changes nothing; and the label
|
|
* naming what a drop does (`dropAction`).
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
import { dropAction, dropPreview } from "../PaneGrid";
|
|
import { pane, split, type PaneLayout } from "../../../lib/paneLayout";
|
|
|
|
const AB: PaneLayout = { root: split("row", pane(0), pane(1)), focus: 0 };
|
|
|
|
describe("dropPreview", () => {
|
|
it("a shown pane leaves its place first: A onto B's top is the whole top half", () => {
|
|
expect(dropPreview(AB, 0, 1, "top")).toEqual({ slot: 0, x: 0, y: 0, w: 1, h: 0.5 });
|
|
});
|
|
|
|
it("a pane from another tab splits the target's current box", () => {
|
|
expect(dropPreview(AB, 2, 1, "bottom")).toEqual({ slot: 2, x: 0.5, y: 0.5, w: 0.5, h: 0.5 });
|
|
});
|
|
|
|
it("the center swaps into the target's box", () => {
|
|
expect(dropPreview(AB, 0, 1, "center")).toEqual({ slot: 0, x: 0.5, y: 0, w: 0.5, h: 1 });
|
|
});
|
|
|
|
it("nothing on its own pane or on a refused move", () => {
|
|
expect(dropPreview(AB, 0, 0, "left")).toBeNull();
|
|
const full: PaneLayout = {
|
|
root: split("row", split("col", pane(0), pane(1)), split("col", pane(2), pane(3))),
|
|
focus: 0,
|
|
};
|
|
expect(dropPreview(full, 5, 0, "left")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("dropAction", () => {
|
|
it("an edge places the pane on that side", () => {
|
|
expect(dropAction(AB, 0, "left")).toBe("panes.drop.left");
|
|
expect(dropAction(AB, 2, "bottom")).toBe("panes.drop.bottom");
|
|
});
|
|
|
|
it("the center swaps a shown pane, or shows one from another tab in its place", () => {
|
|
expect(dropAction(AB, 0, "center")).toBe("panes.drop.swap");
|
|
expect(dropAction(AB, 2, "center")).toBe("panes.drop.replace");
|
|
});
|
|
});
|