@starlove/orbit 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +10 -8
- package/README.md +146 -164
- package/dist/full/csp.txt +1 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
- package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
- package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
- package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
- package/dist/full/fonts/Mona_Sans/README.txt +10 -0
- package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
- package/dist/full/fonts/Newsreader/README.txt +8 -0
- package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
- package/dist/full/index.html +36 -653
- package/dist/full/js/app.BmuponPJ.js +32 -0
- package/dist/full/js/app.BmuponPJ.js.map +1 -0
- package/dist/full/js/chunk.1fJGQGbW.js +2 -0
- package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
- package/dist/full/js/chunk.5D7JO2zp.js +2 -0
- package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
- package/dist/full/js/chunk.BrNn_D79.js +2 -0
- package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
- package/dist/full/js/chunk.CokJXIqM.js +2 -0
- package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
- package/dist/full/js/chunk.D_Haqy6R.js +2 -0
- package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
- package/dist/full/js/chunk.RO0jsH6I.js +15 -0
- package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
- package/dist/full/js/chunk.dqWOEtBM.js +2 -0
- package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
- package/dist/full/js/warm.f260bbbc.js +15 -0
- package/dist/full/orbits-favicon.png +0 -0
- package/dist/full/orbits-logo-bg-wide.png +0 -0
- package/dist/full/orbits-logo-bg.png +0 -0
- package/dist/full/orbits-logo.png +0 -0
- package/dist/full/styles.2b154ffe.css +1 -0
- package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
- package/dist/full/version.json +2 -2
- package/docs/AGENT_GUIDE.md +114 -0
- package/docs/UPDATING.md +249 -64
- package/lib/adapters/indexeddb/blobs.d.ts +3 -0
- package/lib/adapters/indexeddb/blobs.js +54 -0
- package/lib/adapters/indexeddb/database.d.ts +60 -0
- package/lib/adapters/indexeddb/database.js +126 -0
- package/lib/adapters/indexeddb/index.d.ts +5 -0
- package/lib/adapters/indexeddb/index.js +3 -0
- package/lib/adapters/indexeddb/locks.d.ts +14 -0
- package/lib/adapters/indexeddb/locks.js +40 -0
- package/lib/adapters/indexeddb/store.d.ts +40 -0
- package/lib/adapters/indexeddb/store.js +424 -0
- package/lib/adapters/indexeddb/tables.d.ts +43 -0
- package/lib/adapters/indexeddb/tables.js +127 -0
- package/lib/adapters/memory/blobs.d.ts +3 -0
- package/lib/adapters/memory/blobs.js +44 -0
- package/lib/adapters/memory/board.d.ts +41 -0
- package/lib/adapters/memory/board.js +521 -0
- package/lib/adapters/memory/index.d.ts +2 -0
- package/lib/adapters/memory/index.js +2 -0
- package/lib/adapters/memory/search.d.ts +6 -0
- package/lib/adapters/memory/search.js +30 -0
- package/lib/adapters/memory/store.d.ts +34 -0
- package/lib/adapters/memory/store.js +266 -0
- package/lib/adapters/sqlite/backups.d.ts +53 -0
- package/lib/adapters/sqlite/backups.js +232 -0
- package/lib/adapters/sqlite/blobs.d.ts +14 -0
- package/lib/adapters/sqlite/blobs.js +109 -0
- package/lib/adapters/sqlite/board.d.ts +77 -0
- package/lib/adapters/sqlite/board.js +328 -0
- package/lib/adapters/sqlite/database.d.ts +67 -0
- package/lib/adapters/sqlite/database.js +285 -0
- package/lib/adapters/sqlite/index.d.ts +3 -0
- package/lib/adapters/sqlite/index.js +2 -0
- package/lib/adapters/sqlite/lock.d.ts +11 -0
- package/lib/adapters/sqlite/lock.js +34 -0
- package/lib/adapters/sqlite/records.d.ts +54 -0
- package/lib/adapters/sqlite/records.js +546 -0
- package/lib/adapters/sqlite/schema.d.ts +54 -0
- package/lib/adapters/sqlite/schema.js +276 -0
- package/lib/adapters/sqlite/store.d.ts +39 -0
- package/lib/adapters/sqlite/store.js +385 -0
- package/lib/browser/in-page.d.ts +31 -0
- package/lib/browser/in-page.js +46 -0
- package/lib/browser/old-preview.d.ts +13 -0
- package/lib/browser/old-preview.js +80 -0
- package/lib/browser/preview-board.d.ts +8 -0
- package/lib/browser/preview-board.js +69 -0
- package/lib/cli/main.d.ts +7 -0
- package/lib/cli/main.js +343 -0
- package/lib/cli/orbit.d.ts +8 -0
- package/lib/cli/orbit.js +25 -0
- package/lib/client/http.d.ts +38 -0
- package/lib/client/http.js +334 -0
- package/lib/client/local.d.ts +26 -0
- package/lib/client/local.js +190 -0
- package/lib/client/object-url.d.ts +8 -0
- package/lib/client/object-url.js +21 -0
- package/lib/client/types.d.ts +268 -0
- package/lib/client/types.js +8 -0
- package/lib/commands/agent-tools.d.ts +31 -0
- package/lib/commands/agent-tools.js +100 -0
- package/lib/commands/index.d.ts +56 -0
- package/lib/commands/index.js +298 -0
- package/lib/commands/parse.d.ts +73 -0
- package/lib/commands/parse.js +231 -0
- package/lib/commands/present.d.ts +134 -0
- package/lib/commands/present.js +198 -0
- package/lib/commands/session.d.ts +85 -0
- package/lib/commands/session.js +138 -0
- package/lib/commands/verbs.d.ts +35 -0
- package/lib/commands/verbs.js +842 -0
- package/lib/core/actors.d.ts +18 -0
- package/lib/core/actors.js +45 -0
- package/lib/core/agent.d.ts +128 -0
- package/lib/core/agent.js +323 -0
- package/lib/core/boards.d.ts +113 -0
- package/lib/core/boards.js +288 -0
- package/lib/core/bootstrap.d.ts +47 -0
- package/lib/core/bootstrap.js +100 -0
- package/lib/core/bytes.d.ts +10 -0
- package/lib/core/bytes.js +52 -0
- package/lib/core/card-paths.d.ts +25 -0
- package/lib/core/card-paths.js +44 -0
- package/lib/core/context.d.ts +60 -0
- package/lib/core/context.js +42 -0
- package/lib/core/cross-board.d.ts +81 -0
- package/lib/core/cross-board.js +228 -0
- package/lib/core/describe.d.ts +21 -0
- package/lib/core/describe.js +81 -0
- package/lib/core/errors.d.ts +159 -0
- package/lib/core/errors.js +160 -0
- package/lib/core/files.d.ts +79 -0
- package/lib/core/files.js +262 -0
- package/lib/core/history.d.ts +20 -0
- package/lib/core/history.js +53 -0
- package/lib/core/image-types.d.ts +16 -0
- package/lib/core/image-types.js +36 -0
- package/lib/core/model.d.ts +263 -0
- package/lib/core/model.js +34 -0
- package/lib/core/nebula-link.d.ts +14 -0
- package/lib/core/nebula-link.js +39 -0
- package/lib/core/relations.d.ts +58 -0
- package/lib/core/relations.js +112 -0
- package/lib/core/search.d.ts +38 -0
- package/lib/core/search.js +92 -0
- package/lib/core/sessions.d.ts +44 -0
- package/lib/core/sessions.js +158 -0
- package/lib/core/snapshot-schema.d.ts +295 -0
- package/lib/core/snapshot-schema.js +82 -0
- package/lib/core/snapshots.d.ts +75 -0
- package/lib/core/snapshots.js +409 -0
- package/lib/core/states.d.ts +25 -0
- package/lib/core/states.js +126 -0
- package/lib/core/text.d.ts +40 -0
- package/lib/core/text.js +113 -0
- package/lib/core/tickets.d.ts +69 -0
- package/lib/core/tickets.js +311 -0
- package/lib/core/trello.d.ts +17 -0
- package/lib/core/trello.js +269 -0
- package/lib/core/verdicts.d.ts +28 -0
- package/lib/core/verdicts.js +92 -0
- package/lib/core/views.d.ts +95 -0
- package/lib/core/views.js +186 -0
- package/lib/http/app.d.ts +46 -0
- package/lib/http/app.js +491 -0
- package/lib/mcp-server.d.ts +10 -0
- package/lib/mcp-server.js +120 -0
- package/lib/node/assets.d.ts +7 -0
- package/lib/node/assets.js +68 -0
- package/lib/node/data-folder.d.ts +21 -0
- package/lib/node/data-folder.js +34 -0
- package/lib/node/env.d.ts +38 -0
- package/lib/node/env.js +49 -0
- package/lib/node/host-check.d.ts +24 -0
- package/lib/node/host-check.js +53 -0
- package/lib/node/host-services.d.ts +9 -0
- package/lib/node/host-services.js +150 -0
- package/lib/node/local.d.ts +22 -0
- package/lib/node/local.js +61 -0
- package/lib/node/migrate.d.ts +53 -0
- package/lib/node/migrate.js +635 -0
- package/lib/node/package.d.ts +7 -0
- package/lib/node/package.js +14 -0
- package/lib/node/repo-board.d.ts +49 -0
- package/lib/node/repo-board.js +245 -0
- package/lib/node/serve.d.ts +8 -0
- package/lib/node/serve.js +106 -0
- package/lib/orbit.d.ts +47 -0
- package/lib/orbit.js +44 -0
- package/lib/quiet-sqlite-warning.d.ts +1 -0
- package/lib/quiet-sqlite-warning.js +18 -0
- package/lib/store/blob-path.d.ts +6 -0
- package/lib/store/blob-path.js +12 -0
- package/lib/store/conformance.d.ts +52 -0
- package/lib/store/conformance.js +1449 -0
- package/lib/store/search-words.d.ts +10 -0
- package/lib/store/search-words.js +19 -0
- package/lib/store/types.d.ts +452 -0
- package/lib/store/types.js +34 -0
- package/llms.txt +63 -0
- package/package.json +109 -25
- package/src/cli/orbit.js +11 -658
- package/src/mcp-server.js +16 -876
- package/SKILL-ORBIT.md +0 -151
- package/build.mjs +0 -210
- package/dist/full/js/app.62905fcb.js +0 -952
- package/dist/full/js/app.62905fcb.js.map +0 -7
- package/dist/full/styles.4e60d824.css +0 -1
- package/docs/AGENT_PROTOCOL.md +0 -424
- package/docs/MCP_SETUP.md +0 -54
- package/docs/ORBIT_DISPATCH.md +0 -280
- package/docs/SS-Orbit-Dark.png +0 -0
- package/docs/SS-Orbit-Light.png +0 -0
- package/docs/mcp-client.example.json +0 -13
- package/docs/setup/orbit-init-wizard.md +0 -174
- package/public/apple-touch-icon.png +0 -0
- package/public/cursors/live_arrow.cur +0 -0
- package/public/cursors/live_link.cur +0 -0
- package/public/cursors/live_move.cur +0 -0
- package/public/cursors/live_select.cur +0 -0
- package/public/cursors/live_unavail.cur +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/OFL.txt +0 -93
- package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/OFL.txt +0 -93
- package/public/fonts/Inter/README.txt +0 -118
- package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/README.txt +0 -79
- package/public/fonts/OpenRing-Regular.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/README.txt +0 -100
- package/public/fonts/Orbitron/OFL.txt +0 -93
- package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/public/fonts/Orbitron/README.txt +0 -68
- package/public/fonts/Rajdhani/OFL.txt +0 -93
- package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
- package/public/fonts/runescape-uf.ttf +0 -0
- package/public/index.html +0 -769
- package/public/js/actor-labels.js +0 -65
- package/public/js/api.js +0 -39
- package/public/js/app.js +0 -45
- package/public/js/artifact-drawer.js +0 -79
- package/public/js/board-export.js +0 -161
- package/public/js/board-menu.js +0 -374
- package/public/js/card-actions.js +0 -545
- package/public/js/config.js +0 -20
- package/public/js/create-card.js +0 -190
- package/public/js/dom.js +0 -19
- package/public/js/drawer.js +0 -273
- package/public/js/font-preference.js +0 -229
- package/public/js/format.js +0 -433
- package/public/js/help-menu.js +0 -31
- package/public/js/icons.js +0 -16
- package/public/js/kanban.js +0 -694
- package/public/js/local-backend.js +0 -1015
- package/public/js/main.js +0 -161
- package/public/js/modal.js +0 -33
- package/public/js/motion-preference.js +0 -64
- package/public/js/panel.js +0 -58
- package/public/js/router.js +0 -134
- package/public/js/search.js +0 -56
- package/public/js/settings-tabs.js +0 -7
- package/public/js/settings.js +0 -1634
- package/public/js/sse.js +0 -159
- package/public/js/starscape.js +0 -332
- package/public/js/state.js +0 -129
- package/public/js/text-editing.js +0 -128
- package/public/js/theme-preference.js +0 -95
- package/public/js/ticket-detail.js +0 -1516
- package/public/js/ticket-typeahead.js +0 -49
- package/public/js/toast.js +0 -89
- package/public/js/topbar-notes.js +0 -155
- package/public/js/transport.js +0 -22
- package/public/js/typeahead.js +0 -206
- package/public/js/unread.js +0 -121
- package/public/js/update-check.js +0 -58
- package/public/js/url-routes.js +0 -94
- package/public/orbits-favicon.png +0 -0
- package/public/orbits-logo-bg-wide.png +0 -0
- package/public/orbits-logo-bg.png +0 -0
- package/public/orbits-logo.png +0 -0
- package/public/styles.css +0 -7709
- package/src/core/agent.js +0 -387
- package/src/core/agents-md.js +0 -109
- package/src/core/artifacts.js +0 -149
- package/src/core/attachments.js +0 -238
- package/src/core/auth.js +0 -85
- package/src/core/backups.js +0 -142
- package/src/core/board.js +0 -148
- package/src/core/boards.js +0 -409
- package/src/core/bootstrap.js +0 -170
- package/src/core/css-bundle.js +0 -30
- package/src/core/db.js +0 -315
- package/src/core/delete-board.js +0 -163
- package/src/core/dispatch.js +0 -1007
- package/src/core/events.js +0 -314
- package/src/core/http.js +0 -115
- package/src/core/migrate.js +0 -198
- package/src/core/paths.js +0 -55
- package/src/core/provision-repo-board.js +0 -119
- package/src/core/queries.js +0 -246
- package/src/core/registry.js +0 -211
- package/src/core/relations.js +0 -163
- package/src/core/review-verdicts.js +0 -138
- package/src/core/router.js +0 -556
- package/src/core/search.js +0 -183
- package/src/core/seed.js +0 -234
- package/src/core/snapshots.js +0 -692
- package/src/core/states.js +0 -167
- package/src/core/system-picker.js +0 -109
- package/src/core/tickets.js +0 -400
- package/src/core/util.js +0 -106
- package/src/core/version-check.js +0 -154
- package/src/mcp/orbit-client.js +0 -287
- package/src/server.js +0 -115
package/public/js/board-menu.js
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
// Board picker flyout (the "Board" chip in the topbar). Lists every board
|
|
2
|
-
// the actor can see, switches between them, and — for owners — exposes the
|
|
3
|
-
// new-board form. Also owns the topbar chip label/title sync.
|
|
4
|
-
|
|
5
|
-
import { boardFlyout, boardMenuBtn } from "./dom.js";
|
|
6
|
-
import { state } from "./state.js";
|
|
7
|
-
import { escapeHtml } from "./format.js";
|
|
8
|
-
import { api } from "./api.js";
|
|
9
|
-
import { navigate, syncUrlFromState } from "./router.js";
|
|
10
|
-
import { closeCreateFlyout } from "./create-card.js";
|
|
11
|
-
import { toast } from "./toast.js";
|
|
12
|
-
import { load } from "./app.js";
|
|
13
|
-
import { features } from "./config.js";
|
|
14
|
-
import { exportAllBoards } from "./board-export.js";
|
|
15
|
-
|
|
16
|
-
let boardFlyoutView = "list";
|
|
17
|
-
|
|
18
|
-
export function closeBoardFlyout() {
|
|
19
|
-
if (!boardFlyout) return;
|
|
20
|
-
boardFlyout.hidden = true;
|
|
21
|
-
boardFlyout.innerHTML = "";
|
|
22
|
-
boardFlyoutView = "list";
|
|
23
|
-
boardMenuBtn?.setAttribute("aria-expanded", "false");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function closeMenuFlyouts() {
|
|
27
|
-
closeBoardFlyout();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const boardChipIcon = `
|
|
31
|
-
<svg
|
|
32
|
-
class="topbar-icon"
|
|
33
|
-
width="16"
|
|
34
|
-
height="16"
|
|
35
|
-
viewBox="0 0 24 24"
|
|
36
|
-
fill="none"
|
|
37
|
-
aria-hidden="true"
|
|
38
|
-
>
|
|
39
|
-
<rect x="4" y="2" width="6" height="20" rx="0.75" stroke="currentColor" stroke-width="2" />
|
|
40
|
-
<rect x="14" y="2" width="6" height="20" rx="0.75" stroke="currentColor" stroke-width="2" />
|
|
41
|
-
</svg>`;
|
|
42
|
-
|
|
43
|
-
export function updateTopbarChips() {
|
|
44
|
-
if (!state.data) return;
|
|
45
|
-
const board = (state.data.boards || []).find((b) => b.id === state.boardId);
|
|
46
|
-
if (boardMenuBtn) {
|
|
47
|
-
const label = board?.name || "Board";
|
|
48
|
-
boardMenuBtn.innerHTML = `${boardChipIcon}<span class="topbar-chip-label">${escapeHtml(label)}</span>`;
|
|
49
|
-
boardMenuBtn.title = board ? `${board.name} — slug: ${board.slug}` : "Choose board";
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function inflateZipEntry(bytes) {
|
|
54
|
-
if (typeof DecompressionStream === "undefined") {
|
|
55
|
-
throw new Error("Compressed ZIP entries are not supported in this browser.");
|
|
56
|
-
}
|
|
57
|
-
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("deflate-raw"));
|
|
58
|
-
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function parseImportSnapshotText(text, filename = "snapshot") {
|
|
62
|
-
const trimmed = String(text || "").trimStart();
|
|
63
|
-
if (trimmed.startsWith("<")) throw new Error(`${filename} looks like HTML instead of JSON.`);
|
|
64
|
-
try {
|
|
65
|
-
const snapshot = JSON.parse(text);
|
|
66
|
-
if (!snapshot?.board) throw new Error("snapshot_missing_board");
|
|
67
|
-
return snapshot;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
throw new Error(`${filename}: ${error?.message || "invalid JSON"}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function readZipSnapshots(file) {
|
|
74
|
-
const buffer = await file.arrayBuffer();
|
|
75
|
-
const view = new DataView(buffer);
|
|
76
|
-
const bytes = new Uint8Array(buffer);
|
|
77
|
-
const decoder = new TextDecoder();
|
|
78
|
-
const snapshots = [];
|
|
79
|
-
let offset = 0;
|
|
80
|
-
|
|
81
|
-
while (offset + 30 <= bytes.length) {
|
|
82
|
-
const signature = view.getUint32(offset, true);
|
|
83
|
-
if (signature === 0x02014b50 || signature === 0x06054b50) break;
|
|
84
|
-
if (signature !== 0x04034b50) throw new Error("Invalid ZIP archive.");
|
|
85
|
-
|
|
86
|
-
const flags = view.getUint16(offset + 6, true);
|
|
87
|
-
const method = view.getUint16(offset + 8, true);
|
|
88
|
-
const compressedSize = view.getUint32(offset + 18, true);
|
|
89
|
-
const filenameLength = view.getUint16(offset + 26, true);
|
|
90
|
-
const extraLength = view.getUint16(offset + 28, true);
|
|
91
|
-
const nameStart = offset + 30;
|
|
92
|
-
const dataStart = nameStart + filenameLength + extraLength;
|
|
93
|
-
const dataEnd = dataStart + compressedSize;
|
|
94
|
-
if (flags & 0x0008) throw new Error("ZIP entries with data descriptors are not supported.");
|
|
95
|
-
if (dataEnd > bytes.length) throw new Error("Invalid ZIP archive.");
|
|
96
|
-
|
|
97
|
-
const name = decoder.decode(bytes.slice(nameStart, nameStart + filenameLength));
|
|
98
|
-
if (!name.endsWith("/")) {
|
|
99
|
-
if (!name.toLowerCase().endsWith(".orbit.json")) {
|
|
100
|
-
throw new Error("ZIP imports may contain only .orbit.json files.");
|
|
101
|
-
}
|
|
102
|
-
const compressed = bytes.slice(dataStart, dataEnd);
|
|
103
|
-
const data = method === 0 ? compressed : method === 8 ? await inflateZipEntry(compressed) : null;
|
|
104
|
-
if (!data) throw new Error("Unsupported ZIP compression method.");
|
|
105
|
-
snapshots.push(parseImportSnapshotText(decoder.decode(data), name));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
offset = dataEnd;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!snapshots.length) throw new Error("ZIP archive did not contain any .orbit.json snapshots.");
|
|
112
|
-
return snapshots;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function readImportSnapshots(file) {
|
|
116
|
-
const name = String(file?.name || "").toLowerCase();
|
|
117
|
-
if (name.endsWith(".zip")) return readZipSnapshots(file);
|
|
118
|
-
if (name.endsWith(".orbit.json")) return [parseImportSnapshotText(await file.text(), file.name)];
|
|
119
|
-
throw new Error("Choose a .zip archive or a single .orbit.json snapshot.");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function renderBoardFlyoutHeader() {
|
|
123
|
-
const showListButton = boardFlyoutView !== "list";
|
|
124
|
-
const newLabel = showListButton ? "List" : "New";
|
|
125
|
-
return `
|
|
126
|
-
<div class="menu-flyout-head menu-flyout-head--actions">
|
|
127
|
-
<span>Board</span>
|
|
128
|
-
<span class="menu-flyout-actions">
|
|
129
|
-
${features.multiBoard ? `<button type="button" data-variant="ghost" data-board-view="${showListButton ? "list" : "new"}">${newLabel}</button>` : ""}
|
|
130
|
-
<button type="button" data-variant="ghost" data-board-view="export">Export</button>
|
|
131
|
-
</span>
|
|
132
|
-
</div>`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function renderBoardFlyoutInner() {
|
|
136
|
-
const boards = state.data.boards || [];
|
|
137
|
-
const rows = boards
|
|
138
|
-
.map(
|
|
139
|
-
(b) => `
|
|
140
|
-
<button type="button" class="menu-flyout-item ${b.id === state.boardId ? "is-current" : ""}" data-variant="menu-item" data-pick-board="${escapeHtml(b.id)}" role="menuitem">
|
|
141
|
-
<span class="menu-flyout-item-title">${escapeHtml(b.name)}</span>
|
|
142
|
-
<span class="menu-flyout-item-meta">${escapeHtml(b.slug)}</span>
|
|
143
|
-
</button>`
|
|
144
|
-
)
|
|
145
|
-
.join("");
|
|
146
|
-
const listBody =
|
|
147
|
-
rows ||
|
|
148
|
-
`<p class="menu-flyout-hint menu-flyout-hint--empty">No boards yet. Create one below if you can.</p>`;
|
|
149
|
-
const listBlock = `<div class="menu-flyout-list">${listBody}</div>`;
|
|
150
|
-
const createBlock = `
|
|
151
|
-
<div class="menu-flyout-section">
|
|
152
|
-
<h3 class="menu-flyout-heading">New Board</h3>
|
|
153
|
-
<form id="createBoardForm" class="menu-flyout-form">
|
|
154
|
-
<input name="name" type="text" required placeholder="Board name" autocomplete="off" />
|
|
155
|
-
<div class="folder-picker-field">
|
|
156
|
-
<input id="repoPathInput" name="repo_path" type="text" readonly placeholder="Coding project folder (optional)" autocomplete="off" />
|
|
157
|
-
<button type="button" id="pickRepoFolderBtn">Browse</button>
|
|
158
|
-
</div>
|
|
159
|
-
<div id="helperFilesReveal" hidden>
|
|
160
|
-
<p class="menu-flyout-hint menu-flyout-hint--intro">Orbit writes <code>SKILL-ORBIT.md</code> and <code>AGENTS.md</code> into this folder for your AI coding agent.</p>
|
|
161
|
-
<label class="menu-flyout-check">
|
|
162
|
-
<input id="helperFilesCheckbox" name="manage_helper_files" type="checkbox" checked />
|
|
163
|
-
<span>Generate AI helper files (<code>SKILL-ORBIT.md</code>, <code>AGENTS.md</code>)</span>
|
|
164
|
-
</label>
|
|
165
|
-
</div>
|
|
166
|
-
<input name="slug" type="text" placeholder="Slug (optional)" autocomplete="off" />
|
|
167
|
-
<button type="submit">Create board</button>
|
|
168
|
-
</form>
|
|
169
|
-
</div>`;
|
|
170
|
-
const exportBlock = `
|
|
171
|
-
<div class="menu-flyout-section menu-flyout-section--export">
|
|
172
|
-
<p class="menu-flyout-copy">Export every board as Orbit snapshots in a ZIP archive.</p>
|
|
173
|
-
${features.attachments ? `
|
|
174
|
-
<label class="orbit-check menu-flyout-toggle">
|
|
175
|
-
<input type="checkbox" id="exportAllBoardImages" />
|
|
176
|
-
<span class="orbit-check-box" aria-hidden="true">
|
|
177
|
-
<svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
|
|
178
|
-
</span>
|
|
179
|
-
<span class="menu-flyout-toggle-text">Include attached images</span>
|
|
180
|
-
</label>
|
|
181
|
-
` : ""}
|
|
182
|
-
<div class="menu-flyout-actions-row">
|
|
183
|
-
<button type="button" id="exportAllBoards">Export All</button>
|
|
184
|
-
</div>
|
|
185
|
-
<div class="menu-flyout-divider" aria-hidden="true"></div>
|
|
186
|
-
<section class="menu-flyout-danger-section" aria-label="Import All">
|
|
187
|
-
<h3 class="menu-flyout-danger-heading">Import All</h3>
|
|
188
|
-
<p class="menu-flyout-danger-copy">Imports fully overwrite each matching board. Snapshots without a matching board create new boards.</p>
|
|
189
|
-
<div class="menu-flyout-actions-row">
|
|
190
|
-
<input type="file" id="importAllBoardsFile" class="import-file-input" accept=".zip,.orbit.json,application/zip" />
|
|
191
|
-
<button type="button" data-arc="danger" id="importAllBoards">Import All</button>
|
|
192
|
-
</div>
|
|
193
|
-
</section>
|
|
194
|
-
</div>`;
|
|
195
|
-
const body = boardFlyoutView === "export" ? exportBlock : boardFlyoutView === "new" && features.multiBoard ? createBlock : listBlock;
|
|
196
|
-
return `
|
|
197
|
-
${renderBoardFlyoutHeader()}
|
|
198
|
-
${body}
|
|
199
|
-
`;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function rerenderBoardFlyout(view) {
|
|
203
|
-
boardFlyoutView = view;
|
|
204
|
-
boardFlyout.innerHTML = renderBoardFlyoutInner();
|
|
205
|
-
wireBoardFlyout();
|
|
206
|
-
if (view === "new") boardFlyout.querySelector('input[name="name"]')?.focus();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function wireBoardFlyout() {
|
|
210
|
-
boardFlyout.querySelectorAll("[data-board-view]").forEach((button) => {
|
|
211
|
-
button.addEventListener("click", (event) => {
|
|
212
|
-
event.stopPropagation();
|
|
213
|
-
const view = button.getAttribute("data-board-view") || "list";
|
|
214
|
-
rerenderBoardFlyout(view);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
boardFlyout.querySelectorAll("[data-pick-board]").forEach((btn) => {
|
|
219
|
-
btn.addEventListener("click", async (event) => {
|
|
220
|
-
event.stopPropagation();
|
|
221
|
-
const id = btn.getAttribute("data-pick-board");
|
|
222
|
-
closeBoardFlyout();
|
|
223
|
-
if (id === state.boardId) return;
|
|
224
|
-
closeCreateFlyout();
|
|
225
|
-
await navigate({
|
|
226
|
-
boardId: id,
|
|
227
|
-
view: "board"
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
const form = boardFlyout.querySelector("#createBoardForm");
|
|
232
|
-
if (form) {
|
|
233
|
-
const pickRepoFolderBtn = form.querySelector("#pickRepoFolderBtn");
|
|
234
|
-
const repoPathInput = form.querySelector("#repoPathInput");
|
|
235
|
-
const helperFilesReveal = form.querySelector("#helperFilesReveal");
|
|
236
|
-
// The folder explanation and "Generate AI helper files" option only make
|
|
237
|
-
// sense once a folder is chosen — there's nowhere to write them otherwise.
|
|
238
|
-
// Reveal both with the folder value rather than disabling.
|
|
239
|
-
const syncHelperFilesVisibility = () => {
|
|
240
|
-
if (helperFilesReveal) helperFilesReveal.hidden = !repoPathInput.value.trim();
|
|
241
|
-
};
|
|
242
|
-
syncHelperFilesVisibility();
|
|
243
|
-
pickRepoFolderBtn?.addEventListener("click", async (event) => {
|
|
244
|
-
event.preventDefault();
|
|
245
|
-
pickRepoFolderBtn.disabled = true;
|
|
246
|
-
try {
|
|
247
|
-
const result = await api("/api/system/pick-folder", { method: "POST" });
|
|
248
|
-
if (result.path) {
|
|
249
|
-
repoPathInput.value = result.path;
|
|
250
|
-
repoPathInput.dispatchEvent(new Event("input", { bubbles: true }));
|
|
251
|
-
syncHelperFilesVisibility();
|
|
252
|
-
}
|
|
253
|
-
} catch (error) {
|
|
254
|
-
toast.error(error?.message || "Folder picker unavailable");
|
|
255
|
-
} finally {
|
|
256
|
-
pickRepoFolderBtn.disabled = false;
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
form.addEventListener("submit", async (event) => {
|
|
260
|
-
event.preventDefault();
|
|
261
|
-
const fd = new FormData(form);
|
|
262
|
-
const slug = String(fd.get("slug") || "").trim();
|
|
263
|
-
const repoPath = String(fd.get("repo_path") || "").trim();
|
|
264
|
-
// Folder is optional. Only forward the helper-files choice when a folder
|
|
265
|
-
// is set; the server ignores it otherwise.
|
|
266
|
-
const created = await api("/api/boards", {
|
|
267
|
-
method: "POST",
|
|
268
|
-
body: {
|
|
269
|
-
name: fd.get("name"),
|
|
270
|
-
...(repoPath ? { repo_path: repoPath, manage_helper_files: fd.get("manage_helper_files") === "on" } : {}),
|
|
271
|
-
...(slug ? { slug } : {})
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
closeBoardFlyout();
|
|
275
|
-
state.boardId = created.id;
|
|
276
|
-
state.selectedTicketId = "";
|
|
277
|
-
state.detailMode = "settings";
|
|
278
|
-
await load();
|
|
279
|
-
syncUrlFromState();
|
|
280
|
-
toast.success("Board created");
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
boardFlyout.querySelector("#exportAllBoards")?.addEventListener("click", async (event) => {
|
|
285
|
-
const button = event.currentTarget;
|
|
286
|
-
const includeAttachments = Boolean(boardFlyout.querySelector("#exportAllBoardImages")?.checked);
|
|
287
|
-
button.disabled = true;
|
|
288
|
-
const originalText = button.textContent;
|
|
289
|
-
button.textContent = "Exporting...";
|
|
290
|
-
try {
|
|
291
|
-
await exportAllBoards({ includeAttachments });
|
|
292
|
-
} catch (error) {
|
|
293
|
-
toast.error(`Export failed: ${error?.message || "request failed"}`);
|
|
294
|
-
} finally {
|
|
295
|
-
button.disabled = false;
|
|
296
|
-
button.textContent = originalText;
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const importAllBoardsFile = boardFlyout.querySelector("#importAllBoardsFile");
|
|
301
|
-
boardFlyout.querySelector("#importAllBoards")?.addEventListener("click", () => {
|
|
302
|
-
importAllBoardsFile?.click();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
importAllBoardsFile?.addEventListener("change", async (event) => {
|
|
306
|
-
const input = event.currentTarget;
|
|
307
|
-
const file = input.files?.[0];
|
|
308
|
-
if (!file) return;
|
|
309
|
-
const importButton = boardFlyout.querySelector("#importAllBoards");
|
|
310
|
-
const originalText = importButton?.textContent;
|
|
311
|
-
if (importButton) {
|
|
312
|
-
importButton.disabled = true;
|
|
313
|
-
importButton.textContent = "Importing...";
|
|
314
|
-
}
|
|
315
|
-
try {
|
|
316
|
-
const snapshots = await readImportSnapshots(file);
|
|
317
|
-
const boardsById = new Map((state.data.boards || []).map((board) => [board.id, board]));
|
|
318
|
-
const boardsBySlug = new Map((state.data.boards || []).map((board) => [board.slug, board]));
|
|
319
|
-
let replaced = 0;
|
|
320
|
-
let created = 0;
|
|
321
|
-
|
|
322
|
-
for (const snapshot of snapshots) {
|
|
323
|
-
const incomingBoard = snapshot.board || {};
|
|
324
|
-
const match = boardsBySlug.get(incomingBoard.slug) || boardsById.get(incomingBoard.id);
|
|
325
|
-
if (match) {
|
|
326
|
-
await api("/api/admin/import", {
|
|
327
|
-
method: "POST",
|
|
328
|
-
body: {
|
|
329
|
-
board_id: match.id,
|
|
330
|
-
replace_existing: true,
|
|
331
|
-
snapshot
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
replaced += 1;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const result = await api("/api/admin/import", {
|
|
339
|
-
method: "POST",
|
|
340
|
-
body: {
|
|
341
|
-
...(state.boardId ? { board_id: state.boardId } : {}),
|
|
342
|
-
create_new: true,
|
|
343
|
-
snapshot
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
created += 1;
|
|
347
|
-
if (result.imported_board_id) boardsById.set(result.imported_board_id, { id: result.imported_board_id, slug: result.imported_board_slug });
|
|
348
|
-
if (result.imported_board_slug) boardsBySlug.set(result.imported_board_slug, { id: result.imported_board_id, slug: result.imported_board_slug });
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
await load();
|
|
352
|
-
toast.success(`Imported ${snapshots.length} board${snapshots.length === 1 ? "" : "s"} (${replaced} replaced, ${created} created)`);
|
|
353
|
-
closeBoardFlyout();
|
|
354
|
-
await navigate({ boardId: state.boardId, view: "board" }, { replace: true });
|
|
355
|
-
} catch (error) {
|
|
356
|
-
toast.error(`Import failed: ${error?.message || "invalid import file"}`);
|
|
357
|
-
} finally {
|
|
358
|
-
input.value = "";
|
|
359
|
-
if (importButton) {
|
|
360
|
-
importButton.disabled = false;
|
|
361
|
-
importButton.textContent = originalText;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export function openBoardFlyout() {
|
|
368
|
-
closeCreateFlyout();
|
|
369
|
-
if (boardFlyoutView === "new" && !features.multiBoard) boardFlyoutView = "list";
|
|
370
|
-
boardFlyout.innerHTML = renderBoardFlyoutInner();
|
|
371
|
-
boardFlyout.hidden = false;
|
|
372
|
-
boardMenuBtn?.setAttribute("aria-expanded", "true");
|
|
373
|
-
wireBoardFlyout();
|
|
374
|
-
}
|