@starlove/orbit 1.3.4 → 1.4.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 +2 -1
- package/README.md +10 -9
- package/SKILL-ORBIT.md +41 -17
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
- package/dist/full/fonts/Chakra_Petch/OFL.txt +93 -0
- package/dist/full/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/dist/full/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/dist/full/fonts/Inter/OFL.txt +93 -0
- package/dist/full/fonts/Inter/README.txt +118 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
- package/dist/full/fonts/JetBrains_Mono/OFL.txt +93 -0
- package/dist/full/fonts/JetBrains_Mono/README.txt +79 -0
- package/dist/full/fonts/OpenRing-Regular.ttf +0 -0
- package/dist/full/fonts/Open_Sans/OFL.txt +93 -0
- package/dist/full/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/dist/full/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- package/dist/full/fonts/Open_Sans/README.txt +100 -0
- package/dist/full/fonts/Orbitron/OFL.txt +93 -0
- package/dist/full/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/dist/full/fonts/Orbitron/README.txt +68 -0
- package/dist/full/fonts/Rajdhani/OFL.txt +93 -0
- package/dist/full/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
- package/dist/full/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
- package/dist/full/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
- package/dist/full/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
- package/dist/full/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
- package/dist/full/fonts/runescape-uf.ttf +0 -0
- package/dist/full/index.html +137 -27
- package/dist/full/js/app.62905fcb.js +952 -0
- package/dist/full/js/app.62905fcb.js.map +7 -0
- package/dist/full/styles.4e60d824.css +1 -0
- package/dist/full/version.json +2 -2
- package/docs/AGENT_PROTOCOL.md +20 -13
- package/docs/ORBIT_DISPATCH.md +73 -38
- package/docs/UPDATING.md +114 -0
- package/package.json +5 -7
- 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 +93 -0
- 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 +93 -0
- package/public/fonts/Inter/README.txt +118 -0
- 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/OFL.txt +93 -0
- package/public/fonts/JetBrains_Mono/README.txt +79 -0
- package/public/fonts/OpenRing-Regular.ttf +0 -0
- package/public/fonts/Open_Sans/OFL.txt +93 -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 +100 -0
- package/public/fonts/Orbitron/OFL.txt +93 -0
- package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/public/fonts/Orbitron/README.txt +68 -0
- package/public/fonts/Rajdhani/OFL.txt +93 -0
- 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 +135 -25
- package/public/js/artifact-drawer.js +79 -0
- package/public/js/board-export.js +161 -0
- package/public/js/board-menu.js +219 -17
- package/public/js/card-actions.js +1 -1
- package/public/js/create-card.js +60 -23
- package/public/js/drawer.js +164 -4
- package/public/js/font-preference.js +229 -0
- package/public/js/format.js +0 -10
- package/public/js/icons.js +16 -0
- package/public/js/kanban.js +123 -1
- package/public/js/local-backend.js +31 -4
- package/public/js/main.js +7 -1
- package/public/js/modal.js +3 -1
- package/public/js/panel.js +58 -0
- package/public/js/settings-tabs.js +1 -1
- package/public/js/settings.js +606 -275
- package/public/js/sse.js +3 -0
- package/public/js/state.js +1 -1
- package/public/js/text-editing.js +128 -0
- package/public/js/ticket-detail.js +377 -148
- package/public/js/ticket-typeahead.js +49 -0
- package/public/js/toast.js +19 -4
- package/public/js/topbar-notes.js +3 -3
- package/public/js/typeahead.js +206 -0
- package/public/js/update-check.js +58 -0
- package/public/styles.css +5455 -3538
- package/src/cli/orbit.js +9 -5
- package/src/core/agent.js +12 -4
- package/src/core/agents-md.js +3 -3
- package/src/core/artifacts.js +149 -0
- package/src/core/board.js +3 -2
- package/src/core/boards.js +0 -1
- package/src/core/css-bundle.js +1 -0
- package/src/core/db.js +6 -0
- package/src/core/dispatch.js +344 -34
- package/src/core/http.js +9 -2
- package/src/core/paths.js +26 -2
- package/src/core/router.js +33 -0
- package/src/core/search.js +3 -2
- package/src/core/snapshots.js +10 -5
- package/src/core/tickets.js +34 -3
- package/src/core/util.js +3 -2
- package/src/core/version-check.js +154 -0
- package/src/mcp/orbit-client.js +18 -2
- package/src/mcp-server.js +19 -14
- package/src/server.js +28 -3
- package/dist/full/js/app.fb7767dc.js +0 -742
- package/dist/full/js/app.fb7767dc.js.map +0 -7
- package/dist/full/styles.bbc542f5.css +0 -1
|
@@ -17,7 +17,7 @@ const STORES = [
|
|
|
17
17
|
"labels", // {id, board_id, name, color, created_at}
|
|
18
18
|
"tickets", // full ticket row (no joins)
|
|
19
19
|
"ticket_labels", // {ticket_id, label_id} composite key as `${ticket_id}|${label_id}`
|
|
20
|
-
"comments", // {id, ticket_id, author, kind, body, created_at}
|
|
20
|
+
"comments", // {id, ticket_id, author, kind, body, omitted_for_ai, created_at}
|
|
21
21
|
"relations", // {id, source_ticket_id, target_ticket_id, kind, created_at, ...}
|
|
22
22
|
"board_entries", // {id, board_id, type, title, body, ticket_id, struck_at, created_at, updated_at}
|
|
23
23
|
"events", // {id, board_id, ticket_id, type, actor, body_json, created_at}
|
|
@@ -166,7 +166,7 @@ async function seedIfEmpty(db) {
|
|
|
166
166
|
system_path: "",
|
|
167
167
|
default_branch: "main",
|
|
168
168
|
project_notes:
|
|
169
|
-
"This preview lives entirely in your browser (IndexedDB). Use it to try the flow; export the snapshot from Settings →
|
|
169
|
+
"This preview lives entirely in your browser (IndexedDB). Use it to try the flow; export the snapshot from Settings → Data when you want to move to a real Orbit install.",
|
|
170
170
|
agent_instructions:
|
|
171
171
|
"Preview edition has no agent or MCP integration. The full Orbit install (npm i -g starscape-orbit && orbit init) provides those.",
|
|
172
172
|
ai_enabled: 0,
|
|
@@ -210,7 +210,7 @@ async function seedIfEmpty(db) {
|
|
|
210
210
|
description:
|
|
211
211
|
"This onboarding card is for the in-browser preview. Drag it across lanes, edit fields, add comments — it all stays in this browser only.",
|
|
212
212
|
type: "epic",
|
|
213
|
-
aiPlan: "1. Drag this card to In Progress.\n2. Add a comment.\n3. Export the snapshot from Settings →
|
|
213
|
+
aiPlan: "1. Drag this card to In Progress.\n2. Add a comment.\n3. Export the snapshot from Settings → Data.",
|
|
214
214
|
stateId: stateByName.Todo.id,
|
|
215
215
|
priority: 4,
|
|
216
216
|
labels: [labelByName["onboarding"].id, labelByName["needs-decomposition"].id]
|
|
@@ -238,7 +238,7 @@ async function seedIfEmpty(db) {
|
|
|
238
238
|
|
|
239
239
|
addTicket(4, {
|
|
240
240
|
title: "Export your snapshot when ready",
|
|
241
|
-
description: "Settings →
|
|
241
|
+
description: "Settings → Data → Export downloads a JSON snapshot you can import into a real Orbit install.",
|
|
242
242
|
type: "task",
|
|
243
243
|
stateId: stateByName.Backlog.id,
|
|
244
244
|
priority: 1,
|
|
@@ -251,6 +251,7 @@ async function seedIfEmpty(db) {
|
|
|
251
251
|
author: "system",
|
|
252
252
|
kind: "note",
|
|
253
253
|
body: "Welcome to the Orbit preview. Everything lives in your browser.",
|
|
254
|
+
omitted_for_ai: 0,
|
|
254
255
|
created_at: t
|
|
255
256
|
});
|
|
256
257
|
|
|
@@ -618,7 +619,9 @@ async function handleTicketCreate(body) {
|
|
|
618
619
|
type: body.type || (body.parent_ticket_id ? "feature" : "task"),
|
|
619
620
|
parent_ticket_id: body.parent_ticket_id || null,
|
|
620
621
|
ai_plan: body.ai_plan || "",
|
|
622
|
+
plan_artifact_path: body.plan_artifact_path || "",
|
|
621
623
|
implementation_summary: body.implementation_summary || "",
|
|
624
|
+
implementation_artifact_path: body.implementation_artifact_path || "",
|
|
622
625
|
implementation_updates: body.implementation_updates || "",
|
|
623
626
|
state_id: stateId,
|
|
624
627
|
priority: body.priority ?? 0,
|
|
@@ -651,7 +654,9 @@ async function handleTicketPatch(ticketId, body) {
|
|
|
651
654
|
"type",
|
|
652
655
|
"parent_ticket_id",
|
|
653
656
|
"ai_plan",
|
|
657
|
+
"plan_artifact_path",
|
|
654
658
|
"implementation_summary",
|
|
659
|
+
"implementation_artifact_path",
|
|
655
660
|
"implementation_updates",
|
|
656
661
|
"priority"
|
|
657
662
|
];
|
|
@@ -840,6 +845,7 @@ async function handleCommentCreate(ticketId, body) {
|
|
|
840
845
|
author: "you",
|
|
841
846
|
kind: body.kind || "note",
|
|
842
847
|
body: body.body || "",
|
|
848
|
+
omitted_for_ai: 0,
|
|
843
849
|
created_at: time
|
|
844
850
|
};
|
|
845
851
|
stores.comments.put(comment);
|
|
@@ -851,6 +857,26 @@ async function handleCommentCreate(ticketId, body) {
|
|
|
851
857
|
});
|
|
852
858
|
}
|
|
853
859
|
|
|
860
|
+
async function handleCommentPatch(ticketId, commentId, body) {
|
|
861
|
+
const db = await openDb();
|
|
862
|
+
return putAndEvent(db, async (stores) => {
|
|
863
|
+
const ticket = await reqPromise(stores.tickets.get(ticketId));
|
|
864
|
+
if (!ticket) throw err(404, "ticket_not_found");
|
|
865
|
+
const comment = await reqPromise(stores.comments.get(commentId));
|
|
866
|
+
if (!comment || comment.ticket_id !== ticketId) throw err(404, "comment_not_found");
|
|
867
|
+
const omittedForAi = body.omitted_for_ai ?? body.omittedForAi;
|
|
868
|
+
if (typeof omittedForAi !== "boolean") throw err(400, "omitted_for_ai_required");
|
|
869
|
+
const time = nowIso();
|
|
870
|
+
comment.omitted_for_ai = omittedForAi ? 1 : 0;
|
|
871
|
+
ticket.updated_at = time;
|
|
872
|
+
stores.comments.put(comment);
|
|
873
|
+
stores.tickets.put(ticket);
|
|
874
|
+
recordEvent(stores, "comment_ai_omission_changed", ticketId, { comment_id: commentId, omitted_for_ai: omittedForAi });
|
|
875
|
+
await bumpTicketUpdatedAt(stores, ticket.parent_ticket_id, time);
|
|
876
|
+
return comment;
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
|
|
854
880
|
async function handleRelationCreate(body) {
|
|
855
881
|
const db = await openDb();
|
|
856
882
|
return putAndEvent(db, async (stores) => {
|
|
@@ -970,6 +996,7 @@ const ROUTES = [
|
|
|
970
996
|
{ m: "GET", re: /^\/api\/tickets\/([^/]+)\/comments$/, fn: (m) => handleTicketComments(decodeURIComponent(m[1])) },
|
|
971
997
|
{ m: "GET", re: /^\/api\/tickets\/([^/]+)\/history$/, fn: (m) => handleTicketHistory(decodeURIComponent(m[1])) },
|
|
972
998
|
{ m: "POST", re: /^\/api\/tickets\/([^/]+)\/comments$/, fn: (m, u, body) => handleCommentCreate(decodeURIComponent(m[1]), body) },
|
|
999
|
+
{ m: "PATCH", re: /^\/api\/tickets\/([^/]+)\/comments\/([^/]+)$/, fn: (m, u, body) => handleCommentPatch(decodeURIComponent(m[1]), decodeURIComponent(m[2]), body) },
|
|
973
1000
|
{ m: "POST", re: /^\/api\/relations$/, fn: (m, u, body) => handleRelationCreate(body) },
|
|
974
1001
|
{ m: "DELETE", re: /^\/api\/relations\/([^/]+)$/, fn: (m) => handleRelationDelete(decodeURIComponent(m[1])) },
|
|
975
1002
|
{ m: "GET", re: /^\/api\/search$/, fn: (m, u) => handleSearch(u.searchParams.get("q")) },
|
package/public/js/main.js
CHANGED
|
@@ -43,9 +43,12 @@ import { edition } from "./config.js";
|
|
|
43
43
|
import { openModal, wireModal } from "./modal.js";
|
|
44
44
|
import { wireTopbarFocus } from "./topbar-notes.js";
|
|
45
45
|
import { wireHelpMenu } from "./help-menu.js";
|
|
46
|
+
import { checkForUpdate } from "./update-check.js";
|
|
47
|
+
import { applyStoredFontPreferences } from "./font-preference.js";
|
|
46
48
|
|
|
47
49
|
async function init() {
|
|
48
50
|
const welcomeDialog = document.getElementById("welcomeDialog");
|
|
51
|
+
applyStoredFontPreferences();
|
|
49
52
|
wireModal(welcomeDialog);
|
|
50
53
|
wireTopbarFocus();
|
|
51
54
|
wireHelpMenu();
|
|
@@ -97,7 +100,7 @@ async function init() {
|
|
|
97
100
|
const ticket = state.data?.tickets?.find((t) => t.id === ticketId);
|
|
98
101
|
const titleForToast = ticket?.title || "card";
|
|
99
102
|
await api(withBoardQuery(`/api/tickets/${encodeURIComponent(ticketId)}/archive`), { method: "POST" });
|
|
100
|
-
toast.
|
|
103
|
+
toast.removed(`Archived: ${titleForToast}`);
|
|
101
104
|
await load();
|
|
102
105
|
navigate({ boardId: state.boardId, view: "board" });
|
|
103
106
|
});
|
|
@@ -150,6 +153,9 @@ async function init() {
|
|
|
150
153
|
} else {
|
|
151
154
|
syncUrlFromState({ replace: true });
|
|
152
155
|
}
|
|
156
|
+
|
|
157
|
+
// Best-effort, non-blocking: nudge if a newer Orbit is available.
|
|
158
|
+
checkForUpdate();
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
init();
|
package/public/js/modal.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// Escape key is handled natively by the browser; no extra wiring needed.
|
|
9
9
|
|
|
10
10
|
export function openModal(dialogEl) {
|
|
11
|
-
dialogEl
|
|
11
|
+
if (!dialogEl) return;
|
|
12
|
+
dialogEl.showModal();
|
|
13
|
+
dialogEl.querySelector("[data-modal-initial-focus]")?.focus({ preventScroll: true });
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export function closeModal(dialogEl) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Reusable settings-style "panel" primitives shared by the project settings
|
|
2
|
+
// drawer and the ticket detail drawer. A panel is a bordered card with a
|
|
3
|
+
// tinted header strip (small-caps title) and a padded body — the same visual
|
|
4
|
+
// language used across the Appearance / AI / Data settings tabs. The
|
|
5
|
+
// collapsible variant turns the header into a <details>/<summary> toggle with a
|
|
6
|
+
// chevron that flips when open, so any surface can drop in a togglable section
|
|
7
|
+
// without re-deriving the markup. These intentionally emit the .settings-card
|
|
8
|
+
// class family (documented in styles.css as a reusable @starlove/ui primitive).
|
|
9
|
+
|
|
10
|
+
import { escapeHtml } from "./format.js";
|
|
11
|
+
|
|
12
|
+
const panelChevronSvg = `<svg class="settings-card-chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>`;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Static panel: bordered card with a header title and a padded body.
|
|
16
|
+
*
|
|
17
|
+
* Options:
|
|
18
|
+
* - title: heading text shown in the small-caps header strip.
|
|
19
|
+
* - body: raw HTML for the card body (already escaped by the caller).
|
|
20
|
+
* - className: extra classes appended to the .settings-card element.
|
|
21
|
+
* - attrs: extra attributes string placed on the <section> (e.g. data-*).
|
|
22
|
+
* - headExtra: raw HTML appended after the title inside the header (actions).
|
|
23
|
+
*/
|
|
24
|
+
export function renderPanel({ title, body = "", className = "", attrs = "", headExtra = "" } = {}) {
|
|
25
|
+
const classAttr = className ? ` ${className}` : "";
|
|
26
|
+
const extraAttrs = attrs ? ` ${attrs}` : "";
|
|
27
|
+
return `
|
|
28
|
+
<section class="settings-card${classAttr}"${extraAttrs}>
|
|
29
|
+
<header class="settings-card-head">
|
|
30
|
+
<h3 class="settings-card-title">${escapeHtml(title)}</h3>
|
|
31
|
+
${headExtra}
|
|
32
|
+
</header>
|
|
33
|
+
<div class="settings-card-body">${body}</div>
|
|
34
|
+
</section>
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Collapsible panel: same chrome as renderPanel but the header doubles as a
|
|
40
|
+
* <details>/<summary> toggle with a flipping chevron.
|
|
41
|
+
*
|
|
42
|
+
* Options mirror renderPanel, plus:
|
|
43
|
+
* - open: whether the section starts expanded.
|
|
44
|
+
*/
|
|
45
|
+
export function renderTogglePanel({ title, body = "", open = false, className = "", attrs = "", headExtra = "" } = {}) {
|
|
46
|
+
const classAttr = className ? ` ${className}` : "";
|
|
47
|
+
const extraAttrs = attrs ? ` ${attrs}` : "";
|
|
48
|
+
return `
|
|
49
|
+
<details class="settings-card settings-card--collapsible${classAttr}"${open ? " open" : ""}${extraAttrs}>
|
|
50
|
+
<summary class="settings-card-head">
|
|
51
|
+
<h3 class="settings-card-title">${escapeHtml(title)}</h3>
|
|
52
|
+
${headExtra}
|
|
53
|
+
${panelChevronSvg}
|
|
54
|
+
</summary>
|
|
55
|
+
<div class="settings-card-body">${body}</div>
|
|
56
|
+
</details>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Settings drawer tab ids — must match URL segments `#/b/…/settings/:tab` and `settings.js` tab config. */
|
|
2
|
-
export const SETTINGS_TAB_IDS = new Set(["lanes", "appearance", "ai", "notes", "journal", "
|
|
2
|
+
export const SETTINGS_TAB_IDS = new Set(["lanes", "appearance", "ai", "notes", "journal", "data", "archive"]);
|
|
3
3
|
|
|
4
4
|
export function normalizeSettingsTab(raw) {
|
|
5
5
|
if (!raw || !SETTINGS_TAB_IDS.has(raw)) return "lanes";
|