@starlove/orbit 1.0.5 → 1.0.7
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/.dockerignore +0 -0
- package/Dockerfile +0 -0
- package/LICENSE +0 -0
- package/README.md +8 -0
- package/SKILL-ORBIT.md +2 -0
- package/build.mjs +2 -2
- package/dist/full/apple-touch-icon.png +0 -0
- package/dist/full/cursors/live_arrow.cur +0 -0
- package/dist/full/cursors/live_link.cur +0 -0
- package/dist/full/cursors/live_move.cur +0 -0
- package/dist/full/cursors/live_select.cur +0 -0
- package/dist/full/cursors/live_unavail.cur +0 -0
- package/dist/full/favicon-32x32.png +0 -0
- package/dist/full/index.html +2 -2
- package/dist/full/js/app.f4fd5e94.js +604 -0
- package/dist/full/js/app.f4fd5e94.js.map +7 -0
- package/dist/full/orbits-favicon.png +0 -0
- package/dist/full/orbits-logo-bg.png +0 -0
- package/dist/full/orbits-logo.png +0 -0
- package/dist/full/{styles.c2e3d20a.css → styles.14e9dbbd.css} +295 -22
- package/dist/full/version.json +2 -2
- package/docs/AGENT_PROTOCOL.md +4 -0
- package/docs/MCP_SETUP.md +3 -1
- package/docs/SS-Orbit-Dark.png +0 -0
- package/docs/SS-Orbit-Light.png +0 -0
- package/docs/mcp-client.example.json +0 -0
- package/package.json +2 -2
- 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/index.html +0 -0
- package/public/js/api.js +5 -1
- package/public/js/app.js +0 -0
- package/public/js/board-menu.js +0 -0
- package/public/js/config.js +0 -0
- package/public/js/create-card.js +0 -0
- package/public/js/dom.js +0 -0
- package/public/js/drawer.js +2 -0
- package/public/js/format.js +142 -0
- package/public/js/kanban.js +18 -2
- package/public/js/local-backend.js +29 -2
- package/public/js/main.js +14 -6
- package/public/js/modal.js +0 -0
- package/public/js/motion-preference.js +53 -0
- package/public/js/router.js +75 -56
- package/public/js/search.js +0 -0
- package/public/js/settings-tabs.js +1 -1
- package/public/js/settings.js +96 -3
- package/public/js/sse.js +0 -0
- package/public/js/starscape.js +25 -18
- package/public/js/state.js +11 -2
- package/public/js/theme-preference.js +88 -0
- package/public/js/ticket-detail.js +6 -3
- package/public/js/toast.js +0 -0
- package/public/js/transport.js +0 -0
- package/public/js/unread.js +0 -0
- package/public/js/url-routes.js +94 -0
- package/public/orbits-favicon.png +0 -0
- package/public/orbits-logo-bg.png +0 -0
- package/public/orbits-logo.png +0 -0
- package/public/styles.css +295 -22
- package/src/core/agent.js +10 -4
- package/src/core/agents-md.js +1 -0
- package/src/core/auth.js +0 -0
- package/src/core/backups.js +0 -0
- package/src/core/board.js +1 -1
- package/src/core/boards.js +0 -0
- package/src/core/bootstrap.js +10 -2
- package/src/core/db.js +0 -0
- package/src/core/delete-board.js +0 -0
- package/src/core/events.js +0 -0
- package/src/core/http.js +0 -0
- package/src/core/paths.js +0 -0
- package/src/core/provision-repo-board.js +0 -0
- package/src/core/queries.js +0 -0
- package/src/core/registry.js +0 -0
- package/src/core/relations.js +0 -0
- package/src/core/router.js +1 -1
- package/src/core/search.js +62 -23
- package/src/core/seed.js +0 -0
- package/src/core/snapshots.js +0 -0
- package/src/core/states.js +0 -0
- package/src/core/system-picker.js +0 -0
- package/src/core/tickets.js +28 -3
- package/src/core/util.js +0 -0
- package/src/mcp/orbit-client.js +180 -0
- package/src/mcp-server.js +50 -274
- package/src/server.js +0 -0
- package/dist/full/js/app.7e25f59b.js +0 -554
- package/dist/full/js/app.7e25f59b.js.map +0 -7
package/public/js/state.js
CHANGED
|
@@ -10,6 +10,8 @@ export const state = {
|
|
|
10
10
|
/** Currently selected board id. Single-board UI; this points at the active
|
|
11
11
|
* board in `state.data.boards`. */
|
|
12
12
|
boardId: "",
|
|
13
|
+
/** URL-facing board identifier. Routes use slugs; API calls keep using ids. */
|
|
14
|
+
boardSlug: "",
|
|
13
15
|
selectedTicketId: "",
|
|
14
16
|
expandedCardIds: new Set(),
|
|
15
17
|
/** "empty" | "ticket" | "settings" — drives the main right overlay drawer (not the New flyout). */
|
|
@@ -30,9 +32,16 @@ export function getSettingsTab() {
|
|
|
30
32
|
export function syncBoardSelection() {
|
|
31
33
|
if (!state.data) return;
|
|
32
34
|
const boards = state.data.boards || [];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
let board = state.boardId ? boards.find((b) => b.id === state.boardId) : null;
|
|
36
|
+
if (!board && state.boardSlug) {
|
|
37
|
+
board = boards.find((b) => b.slug === state.boardSlug) || boards.find((b) => b.id === state.boardSlug);
|
|
35
38
|
}
|
|
39
|
+
if (!board) {
|
|
40
|
+
const activeBoardId = state.data.active_board_id;
|
|
41
|
+
board = boards.find((b) => b.id === activeBoardId) || boards[0] || null;
|
|
42
|
+
}
|
|
43
|
+
state.boardId = board?.id || "";
|
|
44
|
+
state.boardSlug = board?.slug || "";
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
export function currentBoard() {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const THEME_STORAGE_KEY = "mab_theme";
|
|
2
|
+
export const THEME_CHANGE_EVENT = "orbit:theme-change";
|
|
3
|
+
|
|
4
|
+
const THEMES = new Set(["light", "dark"]);
|
|
5
|
+
|
|
6
|
+
function safeStorage(storage) {
|
|
7
|
+
try {
|
|
8
|
+
return storage ?? globalThis.localStorage ?? null;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function safeDocument(doc) {
|
|
15
|
+
try {
|
|
16
|
+
return doc ?? globalThis.document ?? null;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function safeEventTarget(eventTarget) {
|
|
23
|
+
try {
|
|
24
|
+
return eventTarget ?? globalThis.window ?? null;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizeTheme(theme) {
|
|
31
|
+
return THEMES.has(theme) ? theme : "light";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function storedThemePreference(storage = safeStorage()) {
|
|
35
|
+
const store = safeStorage(storage);
|
|
36
|
+
const value = store?.getItem?.(THEME_STORAGE_KEY);
|
|
37
|
+
return THEMES.has(value) ? value : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function currentTheme({ doc = safeDocument(), storage = safeStorage() } = {}) {
|
|
41
|
+
const rootTheme = safeDocument(doc)?.documentElement?.getAttribute?.("data-theme");
|
|
42
|
+
return THEMES.has(rootTheme) ? rootTheme : storedThemePreference(storage) ?? "light";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function applyStoredThemePreference({
|
|
46
|
+
doc = safeDocument(),
|
|
47
|
+
storage = safeStorage()
|
|
48
|
+
} = {}) {
|
|
49
|
+
const storedTheme = storedThemePreference(storage);
|
|
50
|
+
if (storedTheme) {
|
|
51
|
+
safeDocument(doc)?.documentElement?.setAttribute?.("data-theme", storedTheme);
|
|
52
|
+
}
|
|
53
|
+
return currentTheme({ doc, storage });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function setThemePreference(theme, {
|
|
57
|
+
doc = safeDocument(),
|
|
58
|
+
storage = safeStorage(),
|
|
59
|
+
eventTarget = safeEventTarget(),
|
|
60
|
+
notify = true
|
|
61
|
+
} = {}) {
|
|
62
|
+
const next = normalizeTheme(theme);
|
|
63
|
+
safeDocument(doc)?.documentElement?.setAttribute?.("data-theme", next);
|
|
64
|
+
safeStorage(storage)?.setItem?.(THEME_STORAGE_KEY, next);
|
|
65
|
+
|
|
66
|
+
if (notify) {
|
|
67
|
+
dispatchThemeChange(next, eventTarget);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return next;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function toggleThemePreference(options = {}) {
|
|
74
|
+
const next = currentTheme(options) === "dark" ? "light" : "dark";
|
|
75
|
+
return setThemePreference(next, options);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function dispatchThemeChange(theme, eventTarget = safeEventTarget()) {
|
|
79
|
+
const target = safeEventTarget(eventTarget);
|
|
80
|
+
if (typeof target?.dispatchEvent !== "function") return;
|
|
81
|
+
|
|
82
|
+
if (typeof CustomEvent === "function") {
|
|
83
|
+
target.dispatchEvent(new CustomEvent(THEME_CHANGE_EVENT, { detail: { theme } }));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
target.dispatchEvent({ type: THEME_CHANGE_EVENT, detail: { theme } });
|
|
88
|
+
}
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
typeLabel,
|
|
16
16
|
typeLabelLong,
|
|
17
17
|
priorityLabel,
|
|
18
|
-
priorityKeyFor
|
|
18
|
+
priorityKeyFor,
|
|
19
|
+
renderMarkdown
|
|
19
20
|
} from "./format.js";
|
|
20
21
|
import { renderTypeIcon, renderPriorityPill } from "./kanban.js";
|
|
21
22
|
import { renderDrawerShell, openDrawer, closeDrawer } from "./drawer.js";
|
|
@@ -125,7 +126,8 @@ function wireTicketDetailEditors(ticket) {
|
|
|
125
126
|
|
|
126
127
|
const descEl = drawerInner.querySelector('[data-edit-field="description"]');
|
|
127
128
|
if (descEl) {
|
|
128
|
-
descEl.addEventListener("click", () => {
|
|
129
|
+
descEl.addEventListener("click", (event) => {
|
|
130
|
+
event.preventDefault();
|
|
129
131
|
startInlineEdit(descEl, "description", ticket.description || "", ticketId);
|
|
130
132
|
});
|
|
131
133
|
descEl.addEventListener("keydown", (event) => {
|
|
@@ -270,6 +272,7 @@ export async function renderDetail() {
|
|
|
270
272
|
`;
|
|
271
273
|
|
|
272
274
|
renderDrawerShell({
|
|
275
|
+
mode: "ticket",
|
|
273
276
|
title: ticket.title,
|
|
274
277
|
titleAttrs: {
|
|
275
278
|
class: "editable-field",
|
|
@@ -281,7 +284,7 @@ export async function renderDetail() {
|
|
|
281
284
|
body: `
|
|
282
285
|
<div class="detail-head">
|
|
283
286
|
<p class="detail-updated" title="Last saved change">Updated ${escapeHtml(formatDateDetail(ticket.updated_at))}</p>
|
|
284
|
-
<
|
|
287
|
+
<div class="description markdown-body editable-field ${ticket.description ? "" : "is-placeholder"}" data-edit-field="description" tabindex="0" title="Click to edit">${ticket.description ? renderMarkdown(ticket.description) : escapeHtml("No description yet.")}</div>
|
|
285
288
|
<dl class="ticket-meta" id="ticketMetaGrid" data-ticket-id="${escapeHtml(ticket.id)}">
|
|
286
289
|
<div class="ticket-meta-row">
|
|
287
290
|
<dt>State</dt>
|
package/public/js/toast.js
CHANGED
|
File without changes
|
package/public/js/transport.js
CHANGED
|
File without changes
|
package/public/js/unread.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SETTINGS_TAB_IDS } from "./settings-tabs.js";
|
|
2
|
+
|
|
3
|
+
const EMPTY_ROUTE = Object.freeze({ boardSlug: "", view: "board", ticketId: "", tab: "" });
|
|
4
|
+
|
|
5
|
+
function emptyRoute() {
|
|
6
|
+
return { ...EMPTY_ROUTE };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeLocation(input = globalThis.location) {
|
|
10
|
+
if (typeof input === "string") {
|
|
11
|
+
const url = new URL(input, "http://orbit.local");
|
|
12
|
+
return { pathname: url.pathname || "/", search: url.search || "", hash: url.hash || "" };
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
pathname: input?.pathname || "/",
|
|
16
|
+
search: input?.search || "",
|
|
17
|
+
hash: input?.hash || ""
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function decodeSegment(value) {
|
|
22
|
+
try {
|
|
23
|
+
return decodeURIComponent(value);
|
|
24
|
+
} catch {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function routeSegments(value) {
|
|
30
|
+
return String(value || "")
|
|
31
|
+
.replace(/^#/, "")
|
|
32
|
+
.replace(/^\/+/, "")
|
|
33
|
+
.replace(/\/+$/, "")
|
|
34
|
+
.split("/")
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function routeFromSegments(parts) {
|
|
39
|
+
if (!parts.length) return null;
|
|
40
|
+
|
|
41
|
+
const out = emptyRoute();
|
|
42
|
+
let i = 0;
|
|
43
|
+
|
|
44
|
+
if (parts[i] === "b") {
|
|
45
|
+
if (!parts[i + 1]) return null;
|
|
46
|
+
out.boardSlug = decodeSegment(parts[i + 1]);
|
|
47
|
+
i += 2;
|
|
48
|
+
} else if (parts[i] !== "t" && parts[i] !== "settings") {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (parts[i] === "t" && parts[i + 1]) {
|
|
53
|
+
out.view = "ticket";
|
|
54
|
+
out.ticketId = decodeSegment(parts[i + 1]);
|
|
55
|
+
} else if (parts[i] === "settings") {
|
|
56
|
+
out.view = "settings";
|
|
57
|
+
const tab = parts[i + 1] ? decodeSegment(parts[i + 1]) : "";
|
|
58
|
+
if (tab && SETTINGS_TAB_IDS.has(tab)) out.tab = tab;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function parseLegacyHashRoute(hash) {
|
|
65
|
+
const raw = String(hash || "");
|
|
66
|
+
if (!raw || raw === "#" || raw === "#/") return null;
|
|
67
|
+
return routeFromSegments(routeSegments(raw));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function parseRoute(input = globalThis.location) {
|
|
71
|
+
const loc = normalizeLocation(input);
|
|
72
|
+
return parseLegacyHashRoute(loc.hash) || emptyRoute();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function hasRoute(input = globalThis.location) {
|
|
76
|
+
const loc = normalizeLocation(input);
|
|
77
|
+
return Boolean(parseLegacyHashRoute(loc.hash));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function buildRoute({ boardSlug = "", view = "board", ticketId = "", tab = "" } = {}) {
|
|
81
|
+
const segs = [];
|
|
82
|
+
if (boardSlug) segs.push("b", encodeURIComponent(boardSlug));
|
|
83
|
+
if (view === "ticket" && ticketId) segs.push("t", encodeURIComponent(ticketId));
|
|
84
|
+
else if (view === "settings") {
|
|
85
|
+
segs.push("settings");
|
|
86
|
+
if (tab && SETTINGS_TAB_IDS.has(tab)) segs.push(tab);
|
|
87
|
+
}
|
|
88
|
+
return "#/" + segs.join("/");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isCanonicalRouteUrl(input = globalThis.location, routeObj = parseRoute(input)) {
|
|
92
|
+
const loc = normalizeLocation(input);
|
|
93
|
+
return loc.hash === buildRoute(routeObj);
|
|
94
|
+
}
|
|
File without changes
|
|
File without changes
|
package/public/orbits-logo.png
CHANGED
|
File without changes
|
package/public/styles.css
CHANGED
|
@@ -76,6 +76,7 @@ input[type="file"] {
|
|
|
76
76
|
--orbit-add-border: rgba(29, 37, 40, 0.12);
|
|
77
77
|
--orbit-phantom-border: rgba(29, 37, 40, 0.14);
|
|
78
78
|
--orbit-phantom-bg: rgba(29, 37, 40, 0.03);
|
|
79
|
+
--kanban-column-width: 22rem;
|
|
79
80
|
--scrollbar-thumb: rgba(var(--accent-rgb),0.45);
|
|
80
81
|
--scrollbar-thumb-hover: rgba(var(--accent-rgb),0.65);
|
|
81
82
|
--scrollbar-track: transparent;
|
|
@@ -100,6 +101,12 @@ input[type="file"] {
|
|
|
100
101
|
--select-chevron-size: 12px;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
@media (max-width: 720px) {
|
|
105
|
+
:root {
|
|
106
|
+
--kanban-column-width: 18rem;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
* {
|
|
104
111
|
box-sizing: border-box;
|
|
105
112
|
}
|
|
@@ -529,6 +536,12 @@ h1 {
|
|
|
529
536
|
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
|
530
537
|
}
|
|
531
538
|
|
|
539
|
+
#boardFlyout {
|
|
540
|
+
min-width: 20.25rem;
|
|
541
|
+
max-width: min(30rem, calc(100vw - 1.5rem));
|
|
542
|
+
max-height: min(36rem, calc(100vh - 4rem));
|
|
543
|
+
}
|
|
544
|
+
|
|
532
545
|
.menu-flyout--align-end {
|
|
533
546
|
right: 0;
|
|
534
547
|
left: auto;
|
|
@@ -1109,7 +1122,7 @@ h1 {
|
|
|
1109
1122
|
/* Horizontal scroll: styled thumb/track (Firefox + WebKit). */
|
|
1110
1123
|
.kanban {
|
|
1111
1124
|
display: grid;
|
|
1112
|
-
grid-auto-columns: minmax(
|
|
1125
|
+
grid-auto-columns: minmax(var(--kanban-column-width), 1fr);
|
|
1113
1126
|
grid-auto-flow: column;
|
|
1114
1127
|
grid-template-rows: minmax(0, 1fr);
|
|
1115
1128
|
flex: 1 1 auto;
|
|
@@ -1704,8 +1717,8 @@ h1 {
|
|
|
1704
1717
|
/* Epic-aware grouping inside a column.
|
|
1705
1718
|
`.epic-children` is a flex item nested in `.column` (which is itself flex-column,
|
|
1706
1719
|
gap 0.7rem). Right-aligning + narrowing it makes its cards read as indented
|
|
1707
|
-
children of the epic card or `.epic-mini-header` directly above.
|
|
1708
|
-
margin-top tightens the visual link by halving the column gap. */
|
|
1720
|
+
children of the epic card or full-width `.epic-mini-header` directly above.
|
|
1721
|
+
The negative margin-top tightens the visual link by halving the column gap. */
|
|
1709
1722
|
.epic-children {
|
|
1710
1723
|
display: flex;
|
|
1711
1724
|
flex-direction: column;
|
|
@@ -1744,15 +1757,14 @@ h1 {
|
|
|
1744
1757
|
0 8px 20px rgba(0, 0, 0, 0.25);
|
|
1745
1758
|
}
|
|
1746
1759
|
|
|
1747
|
-
/* Thin "header" that stands in for the epic when its children sit in
|
|
1748
|
-
column.
|
|
1749
|
-
|
|
1760
|
+
/* Thin full-width "header" that stands in for the epic when its children sit in
|
|
1761
|
+
a different column. Children remain narrowed underneath so the hierarchy is
|
|
1762
|
+
visible without cutting the lane width at the epic anchor. */
|
|
1750
1763
|
.epic-mini-header {
|
|
1751
1764
|
display: flex;
|
|
1752
1765
|
align-items: center;
|
|
1753
1766
|
gap: 0.4rem;
|
|
1754
|
-
width:
|
|
1755
|
-
align-self: flex-end;
|
|
1767
|
+
width: 100%;
|
|
1756
1768
|
padding: 0.45rem 0.6rem;
|
|
1757
1769
|
border-radius: 10px 10px 4px 4px;
|
|
1758
1770
|
background: rgba(var(--purple-rgb), 0.10);
|
|
@@ -2144,7 +2156,13 @@ h1 {
|
|
|
2144
2156
|
background: var(--drawer-bg);
|
|
2145
2157
|
box-shadow: 0 24px 60px rgba(12, 18, 26, 0.28);
|
|
2146
2158
|
transform: translateX(calc(100% + 1.5rem));
|
|
2147
|
-
transition:
|
|
2159
|
+
transition:
|
|
2160
|
+
transform 320ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
2161
|
+
width 180ms ease;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
.drawer.is-wide {
|
|
2165
|
+
width: min(58rem, calc(100vw - 1rem));
|
|
2148
2166
|
}
|
|
2149
2167
|
|
|
2150
2168
|
.drawer.is-open {
|
|
@@ -2286,8 +2304,8 @@ h1 {
|
|
|
2286
2304
|
border-color: var(--accent-border);
|
|
2287
2305
|
}
|
|
2288
2306
|
|
|
2289
|
-
.drawer-close
|
|
2290
|
-
|
|
2307
|
+
.drawer-close,
|
|
2308
|
+
.drawer-archive {
|
|
2291
2309
|
display: inline-grid;
|
|
2292
2310
|
place-items: center;
|
|
2293
2311
|
width: 2rem;
|
|
@@ -2297,10 +2315,14 @@ h1 {
|
|
|
2297
2315
|
border-radius: 10px;
|
|
2298
2316
|
background: var(--surface);
|
|
2299
2317
|
color: var(--ink);
|
|
2318
|
+
cursor: url('cursors/live_link.cur'), pointer;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
.drawer-close {
|
|
2322
|
+
grid-area: close;
|
|
2300
2323
|
font-size: 1.35rem;
|
|
2301
2324
|
font-weight: 400;
|
|
2302
2325
|
line-height: 1;
|
|
2303
|
-
cursor: url('cursors/live_link.cur'), pointer;
|
|
2304
2326
|
}
|
|
2305
2327
|
|
|
2306
2328
|
.drawer-close:hover {
|
|
@@ -2310,16 +2332,6 @@ h1 {
|
|
|
2310
2332
|
|
|
2311
2333
|
.drawer-archive {
|
|
2312
2334
|
grid-area: archive;
|
|
2313
|
-
display: inline-grid;
|
|
2314
|
-
place-items: center;
|
|
2315
|
-
width: 2rem;
|
|
2316
|
-
height: 2rem;
|
|
2317
|
-
padding: 0;
|
|
2318
|
-
border: 1px solid var(--line);
|
|
2319
|
-
border-radius: 10px;
|
|
2320
|
-
background: var(--surface);
|
|
2321
|
-
color: var(--ink);
|
|
2322
|
-
cursor: url('cursors/live_link.cur'), pointer;
|
|
2323
2335
|
}
|
|
2324
2336
|
|
|
2325
2337
|
.drawer-archive:hover {
|
|
@@ -2463,6 +2475,95 @@ h1 {
|
|
|
2463
2475
|
white-space: pre-wrap;
|
|
2464
2476
|
}
|
|
2465
2477
|
|
|
2478
|
+
.markdown-body {
|
|
2479
|
+
white-space: normal;
|
|
2480
|
+
overflow-wrap: anywhere;
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
.markdown-body p,
|
|
2484
|
+
.markdown-body ul,
|
|
2485
|
+
.markdown-body ol,
|
|
2486
|
+
.markdown-body pre {
|
|
2487
|
+
margin: 0;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
.markdown-body p + p,
|
|
2491
|
+
.markdown-body p + ul,
|
|
2492
|
+
.markdown-body p + ol,
|
|
2493
|
+
.markdown-body p + pre,
|
|
2494
|
+
.markdown-body ul + p,
|
|
2495
|
+
.markdown-body ul + ol,
|
|
2496
|
+
.markdown-body ul + pre,
|
|
2497
|
+
.markdown-body ol + p,
|
|
2498
|
+
.markdown-body ol + ul,
|
|
2499
|
+
.markdown-body ol + pre,
|
|
2500
|
+
.markdown-body pre + p,
|
|
2501
|
+
.markdown-body pre + ul,
|
|
2502
|
+
.markdown-body pre + ol {
|
|
2503
|
+
margin-top: 0.55rem;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
.markdown-body ul,
|
|
2507
|
+
.markdown-body ol {
|
|
2508
|
+
padding-left: 1.25rem;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
.markdown-body li + li {
|
|
2512
|
+
margin-top: 0.2rem;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
.markdown-body code {
|
|
2516
|
+
border: 1px solid rgba(var(--blue-rgb), 0.18);
|
|
2517
|
+
border-radius: 0.35rem;
|
|
2518
|
+
padding: 0.05rem 0.25rem;
|
|
2519
|
+
color: var(--ink);
|
|
2520
|
+
background: rgba(var(--blue-rgb), 0.08);
|
|
2521
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
2522
|
+
font-size: 0.9em;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
.markdown-body pre {
|
|
2526
|
+
padding: 0.65rem 0.75rem;
|
|
2527
|
+
overflow: auto;
|
|
2528
|
+
border: 1px solid var(--line);
|
|
2529
|
+
border-radius: 10px;
|
|
2530
|
+
background: var(--surface-card);
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
.markdown-body pre code {
|
|
2534
|
+
display: block;
|
|
2535
|
+
border: 0;
|
|
2536
|
+
padding: 0;
|
|
2537
|
+
background: transparent;
|
|
2538
|
+
white-space: pre;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
.markdown-body a {
|
|
2542
|
+
color: var(--blue);
|
|
2543
|
+
font-weight: 700;
|
|
2544
|
+
text-decoration: underline;
|
|
2545
|
+
text-decoration-thickness: 0.08em;
|
|
2546
|
+
text-underline-offset: 0.15em;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
.card-description {
|
|
2550
|
+
margin-top: 0.45rem;
|
|
2551
|
+
color: var(--muted);
|
|
2552
|
+
font-size: 0.82rem;
|
|
2553
|
+
line-height: 1.45;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
.card .markdown-body p {
|
|
2557
|
+
display: block;
|
|
2558
|
+
overflow: visible;
|
|
2559
|
+
-webkit-line-clamp: unset;
|
|
2560
|
+
-webkit-box-orient: initial;
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
.card .markdown-body pre {
|
|
2564
|
+
max-height: 8rem;
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2466
2567
|
/* The detail pane's lede description is body copy, not a card excerpt — give
|
|
2467
2568
|
it more presence than the universal muted .description rule. */
|
|
2468
2569
|
.detail-head > .description {
|
|
@@ -3194,6 +3295,157 @@ details.section > summary .mcp-title-row {
|
|
|
3194
3295
|
margin-bottom: 0.85rem;
|
|
3195
3296
|
}
|
|
3196
3297
|
|
|
3298
|
+
.appearance-theme-section {
|
|
3299
|
+
margin-top: 0.4rem;
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
.appearance-theme-section + .section:has(> .orbit-check:first-child) {
|
|
3303
|
+
margin-top: 1.25rem;
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
.appearance-theme-field {
|
|
3307
|
+
display: grid;
|
|
3308
|
+
gap: 0.55rem;
|
|
3309
|
+
min-width: 0;
|
|
3310
|
+
margin: 0;
|
|
3311
|
+
padding: 0;
|
|
3312
|
+
border: 0;
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
.appearance-theme-field legend {
|
|
3316
|
+
margin-bottom: 0.55rem;
|
|
3317
|
+
color: var(--ink);
|
|
3318
|
+
font-size: 0.88rem;
|
|
3319
|
+
font-weight: 800;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
.appearance-theme-options {
|
|
3323
|
+
display: grid;
|
|
3324
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3325
|
+
gap: 0.5rem;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
.appearance-theme-option {
|
|
3329
|
+
position: relative;
|
|
3330
|
+
display: inline-flex;
|
|
3331
|
+
min-width: 0;
|
|
3332
|
+
min-height: 2.5rem;
|
|
3333
|
+
align-items: center;
|
|
3334
|
+
justify-content: center;
|
|
3335
|
+
gap: 0.45rem;
|
|
3336
|
+
padding: 0.5rem 0.72rem;
|
|
3337
|
+
border: 1px solid var(--line);
|
|
3338
|
+
border-radius: 10px;
|
|
3339
|
+
background: var(--surface-dim);
|
|
3340
|
+
color: var(--ink);
|
|
3341
|
+
font-size: 0.86rem;
|
|
3342
|
+
font-weight: 700;
|
|
3343
|
+
cursor: url('cursors/live_link.cur'), pointer;
|
|
3344
|
+
transition:
|
|
3345
|
+
border-color 160ms ease,
|
|
3346
|
+
background 160ms ease,
|
|
3347
|
+
box-shadow 160ms ease,
|
|
3348
|
+
color 160ms ease;
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
.appearance-theme-option:hover {
|
|
3352
|
+
border-color: var(--accent-border);
|
|
3353
|
+
background: var(--accent-hover);
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
.appearance-theme-option input[type="radio"] {
|
|
3357
|
+
position: absolute;
|
|
3358
|
+
width: 1px;
|
|
3359
|
+
height: 1px;
|
|
3360
|
+
padding: 0;
|
|
3361
|
+
margin: -1px;
|
|
3362
|
+
overflow: hidden;
|
|
3363
|
+
clip: rect(0, 0, 0, 0);
|
|
3364
|
+
white-space: nowrap;
|
|
3365
|
+
border: 0;
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
.appearance-theme-option-mark {
|
|
3369
|
+
display: grid;
|
|
3370
|
+
width: 1.35rem;
|
|
3371
|
+
height: 1.35rem;
|
|
3372
|
+
flex: 0 0 auto;
|
|
3373
|
+
place-items: center;
|
|
3374
|
+
border: 1px solid var(--accent-border);
|
|
3375
|
+
border-radius: 999px;
|
|
3376
|
+
background: var(--surface-input);
|
|
3377
|
+
transition:
|
|
3378
|
+
background 160ms ease,
|
|
3379
|
+
border-color 160ms ease,
|
|
3380
|
+
box-shadow 160ms ease;
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
.appearance-theme-option-icon {
|
|
3384
|
+
width: 0.9rem;
|
|
3385
|
+
height: 0.9rem;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
.appearance-theme-option-label {
|
|
3389
|
+
min-width: 0;
|
|
3390
|
+
overflow: hidden;
|
|
3391
|
+
text-overflow: ellipsis;
|
|
3392
|
+
white-space: nowrap;
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
.appearance-theme-option:has(input[type="radio"]:checked) {
|
|
3396
|
+
border-color: var(--accent);
|
|
3397
|
+
background:
|
|
3398
|
+
linear-gradient(135deg, rgba(var(--accent-rgb),0.12), rgba(var(--blue-rgb),0.08)),
|
|
3399
|
+
var(--surface-dim);
|
|
3400
|
+
box-shadow:
|
|
3401
|
+
inset 0 0 0 1px rgba(var(--accent-rgb),0.18),
|
|
3402
|
+
0 4px 10px rgba(var(--accent-rgb),0.12);
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
.appearance-theme-option:has(input[type="radio"]:checked) .appearance-theme-option-mark {
|
|
3406
|
+
border-color: var(--accent);
|
|
3407
|
+
color: #fff;
|
|
3408
|
+
background:
|
|
3409
|
+
linear-gradient(135deg, rgba(var(--accent-rgb),0.95), rgba(var(--blue-rgb),0.95));
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
.appearance-theme-option input[type="radio"]:focus-visible ~ .appearance-theme-option-mark {
|
|
3413
|
+
box-shadow: var(--accent-ring);
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
[data-theme="dark"] .appearance-theme-option {
|
|
3417
|
+
background: rgba(15, 25, 50, 0.55);
|
|
3418
|
+
border-color: rgba(var(--starlight-rgb),0.16);
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
[data-theme="dark"] .appearance-theme-option:hover {
|
|
3422
|
+
border-color: rgba(var(--starlight-rgb),0.4);
|
|
3423
|
+
background: rgba(var(--starlight-rgb),0.08);
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
[data-theme="dark"] .appearance-theme-option-mark {
|
|
3427
|
+
border-color: rgba(var(--starlight-rgb),0.35);
|
|
3428
|
+
background:
|
|
3429
|
+
linear-gradient(135deg, rgba(var(--starlight-rgb),0.06), rgba(var(--cosmos-rgb),0) 65%),
|
|
3430
|
+
rgba(15, 25, 50, 0.7);
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
[data-theme="dark"] .appearance-theme-option:has(input[type="radio"]:checked) {
|
|
3434
|
+
border-color: rgba(var(--starlight-rgb),0.55);
|
|
3435
|
+
background:
|
|
3436
|
+
linear-gradient(135deg, rgba(var(--starlight-rgb),0.13), rgba(var(--violet-rgb),0.08)),
|
|
3437
|
+
rgba(15, 25, 50, 0.65);
|
|
3438
|
+
box-shadow:
|
|
3439
|
+
inset 0 0 0 1px rgba(var(--starlight-rgb),0.18),
|
|
3440
|
+
0 4px 14px rgba(var(--starlight-rgb),0.16);
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
[data-theme="dark"] .appearance-theme-option:has(input[type="radio"]:checked) .appearance-theme-option-mark {
|
|
3444
|
+
border-color: rgba(var(--starlight-rgb),0.7);
|
|
3445
|
+
color: #0a1024;
|
|
3446
|
+
background: linear-gradient(135deg, #d6e6ff, #a8c0ff);
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3197
3449
|
/* Lanes tab opens directly with the checkbox — drop the default .section
|
|
3198
3450
|
top margin so it sits flush under the drawer tabs. */
|
|
3199
3451
|
.section:has(> .orbit-check:first-child) {
|
|
@@ -4828,6 +5080,27 @@ button:disabled,
|
|
|
4828
5080
|
}
|
|
4829
5081
|
}
|
|
4830
5082
|
|
|
5083
|
+
[data-reduced-motion="reduce"] *,
|
|
5084
|
+
[data-reduced-motion="reduce"] *::before,
|
|
5085
|
+
[data-reduced-motion="reduce"] *::after {
|
|
5086
|
+
scroll-behavior: auto !important;
|
|
5087
|
+
transition-duration: 0.01ms !important;
|
|
5088
|
+
animation-duration: 0.01ms !important;
|
|
5089
|
+
animation-iteration-count: 1 !important;
|
|
5090
|
+
}
|
|
5091
|
+
|
|
5092
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .starfield,
|
|
5093
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .milky-way,
|
|
5094
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .grain::before,
|
|
5095
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .grain::after,
|
|
5096
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .signature-star {
|
|
5097
|
+
animation: none;
|
|
5098
|
+
}
|
|
5099
|
+
|
|
5100
|
+
[data-reduced-motion="reduce"][data-theme="dark"] .meteor {
|
|
5101
|
+
display: none;
|
|
5102
|
+
}
|
|
5103
|
+
|
|
4831
5104
|
[data-theme="dark"] {
|
|
4832
5105
|
/* Tells the browser to render native widgets (dropdowns, scrollbars,
|
|
4833
5106
|
date pickers, etc.) in their dark variant so option popups match. */
|