@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/create-card.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
// "New card" flyout (top-right panel, separate from the main ticket
|
|
2
|
-
// drawer). Handles open/close transitions, form rendering, and submission.
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
$,
|
|
6
|
-
createFlyout,
|
|
7
|
-
createFlyoutInner,
|
|
8
|
-
createFlyoutBackdrop
|
|
9
|
-
} from "./dom.js";
|
|
10
|
-
import { state, visibleStatesForProject, ticketsForProject } from "./state.js";
|
|
11
|
-
import { escapeHtml } from "./format.js";
|
|
12
|
-
import { api } from "./api.js";
|
|
13
|
-
import { syncUrlFromState } from "./router.js";
|
|
14
|
-
import { handleTextareaIndentationKeydown } from "./text-editing.js";
|
|
15
|
-
import { closeDrawer } from "./drawer.js";
|
|
16
|
-
import { closeMenuFlyouts } from "./board-menu.js";
|
|
17
|
-
import { load } from "./app.js";
|
|
18
|
-
import { closeIconSvg } from "./icons.js";
|
|
19
|
-
import { attachTicketTypeahead, ticketCanonicalLabel } from "./ticket-typeahead.js";
|
|
20
|
-
|
|
21
|
-
/** Clears any pending hide timeout when reopening the create flyout quickly. */
|
|
22
|
-
let createFlyoutHideTimer = 0;
|
|
23
|
-
|
|
24
|
-
export function isCreateFlyoutOpen() {
|
|
25
|
-
return createFlyout?.classList.contains("is-open");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Compact top-right panel for creating a card (separate from the main ticket drawer). */
|
|
29
|
-
export function openCreateFlyout(preselectedLaneId) {
|
|
30
|
-
clearTimeout(createFlyoutHideTimer);
|
|
31
|
-
closeMenuFlyouts();
|
|
32
|
-
closeDrawer();
|
|
33
|
-
createFlyoutInner.innerHTML = renderCreateForm(preselectedLaneId || "");
|
|
34
|
-
wireCreateForm();
|
|
35
|
-
$("#createFlyoutClose")?.addEventListener("click", () => closeCreateFlyout());
|
|
36
|
-
createFlyout.hidden = false;
|
|
37
|
-
createFlyoutBackdrop.hidden = false;
|
|
38
|
-
requestAnimationFrame(() => {
|
|
39
|
-
createFlyout.classList.add("is-open");
|
|
40
|
-
createFlyoutBackdrop.classList.add("is-visible");
|
|
41
|
-
});
|
|
42
|
-
requestAnimationFrame(() => {
|
|
43
|
-
$("#createTicketTitle")?.focus();
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function closeCreateFlyout() {
|
|
48
|
-
clearTimeout(createFlyoutHideTimer);
|
|
49
|
-
createFlyout.classList.remove("is-open");
|
|
50
|
-
createFlyoutBackdrop.classList.remove("is-visible");
|
|
51
|
-
createFlyoutHideTimer = setTimeout(() => {
|
|
52
|
-
createFlyout.hidden = true;
|
|
53
|
-
createFlyoutBackdrop.hidden = true;
|
|
54
|
-
createFlyoutInner.innerHTML = "";
|
|
55
|
-
}, 220);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** HTML for the “New” flyout: lane + type + priority on one row, then parent and labels. */
|
|
59
|
-
function renderCreateForm(preselectedLaneId) {
|
|
60
|
-
const states = visibleStatesForProject();
|
|
61
|
-
const defaultState = states.find((item) => item.is_default) || states[0];
|
|
62
|
-
const effectiveStateId =
|
|
63
|
-
preselectedLaneId && states.some((s) => s.id === preselectedLaneId)
|
|
64
|
-
? preselectedLaneId
|
|
65
|
-
: defaultState?.id || "";
|
|
66
|
-
const stateOptions = states
|
|
67
|
-
.map(
|
|
68
|
-
(item) =>
|
|
69
|
-
`<option value="${escapeHtml(item.id)}" ${item.id === effectiveStateId ? "selected" : ""}>${escapeHtml(item.name)}</option>`
|
|
70
|
-
)
|
|
71
|
-
.join("");
|
|
72
|
-
return `
|
|
73
|
-
<div class="create-flyout-head">
|
|
74
|
-
<h2>New card</h2>
|
|
75
|
-
<button type="button" class="create-flyout-close ghost" id="createFlyoutClose" aria-label="Close">${closeIconSvg}</button>
|
|
76
|
-
</div>
|
|
77
|
-
<form id="createTicketForm" class="create-flyout-form">
|
|
78
|
-
<input id="createTicketTitle" name="title" type="text" placeholder="Title — what is this card?" autocomplete="off" required />
|
|
79
|
-
<textarea id="createTicketDescription" name="description" placeholder="Description (optional)"></textarea>
|
|
80
|
-
<div class="control-grid">
|
|
81
|
-
<label class="control-field">
|
|
82
|
-
<span class="control-field-label">LANE</span>
|
|
83
|
-
<select id="createTicketState" name="state" class="select-chevron-field" aria-label="Lane">${stateOptions}</select>
|
|
84
|
-
</label>
|
|
85
|
-
<label class="control-field">
|
|
86
|
-
<span class="control-field-label">TYPE</span>
|
|
87
|
-
<select id="createTicketType" name="type" class="select-chevron-field" aria-label="Ticket type">
|
|
88
|
-
<option value="epic">Epic</option>
|
|
89
|
-
<option value="feature">Feature</option>
|
|
90
|
-
<option value="task" selected>Task</option>
|
|
91
|
-
<option value="bug">Bug</option>
|
|
92
|
-
</select>
|
|
93
|
-
</label>
|
|
94
|
-
<label class="control-field">
|
|
95
|
-
<span class="control-field-label">PRIORITY</span>
|
|
96
|
-
<select id="createTicketPriority" name="priority" class="select-chevron-field" aria-label="Priority">
|
|
97
|
-
<option value="4">URGENT</option>
|
|
98
|
-
<option value="3">HIGH</option>
|
|
99
|
-
<option value="2" selected>MED</option>
|
|
100
|
-
<option value="1">LOW</option>
|
|
101
|
-
<option value="0">MAYBE</option>
|
|
102
|
-
</select>
|
|
103
|
-
</label>
|
|
104
|
-
</div>
|
|
105
|
-
<label class="control-field" id="createTicketParentField">
|
|
106
|
-
<span class="control-field-label">Parent epic</span>
|
|
107
|
-
<input id="createTicketParent" name="parent_target" type="text" placeholder="Search epic… (optional)" autocomplete="off" aria-label="Parent epic" />
|
|
108
|
-
<input type="hidden" name="parent_ticket_id" id="createTicketParentId" value="" />
|
|
109
|
-
</label>
|
|
110
|
-
<input id="createTicketLabels" name="labels" type="text" placeholder="Labels (comma-separated)" />
|
|
111
|
-
<button type="submit">Create card</button>
|
|
112
|
-
</form>
|
|
113
|
-
`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function wireCreateForm() {
|
|
117
|
-
const form = $("#createTicketForm");
|
|
118
|
-
if (!form) return;
|
|
119
|
-
const description = form.querySelector("#createTicketDescription");
|
|
120
|
-
description?.addEventListener("keydown", (event) => {
|
|
121
|
-
handleTextareaIndentationKeydown(event, description);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Parent epic is chosen via the shared styled typeahead. The visible field
|
|
125
|
-
// holds the "#12 — Title" label; the resolved epic id lives in a hidden input
|
|
126
|
-
// that the submit reads. Typing (or clearing) drops a stale id so only an
|
|
127
|
-
// explicit pick is submitted.
|
|
128
|
-
const parentInput = form.querySelector("#createTicketParent");
|
|
129
|
-
const parentId = form.querySelector("#createTicketParentId");
|
|
130
|
-
parentInput.addEventListener("input", () => {
|
|
131
|
-
parentId.value = "";
|
|
132
|
-
});
|
|
133
|
-
attachTicketTypeahead(
|
|
134
|
-
parentInput,
|
|
135
|
-
() => ticketsForProject().filter((ticket) => ticket.type === "epic"),
|
|
136
|
-
{
|
|
137
|
-
onSelect: (ticket) => {
|
|
138
|
-
parentId.value = ticket.id;
|
|
139
|
-
parentInput.value = ticketCanonicalLabel(ticket);
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
// Epics cannot have a parent (server rejects with `epic_cannot_have_parent`),
|
|
145
|
-
// so hide the Parent epic field whenever Type is Epic.
|
|
146
|
-
const typeSelect = form.querySelector("#createTicketType");
|
|
147
|
-
const parentField = form.querySelector("#createTicketParentField");
|
|
148
|
-
const syncParentVisibility = () => {
|
|
149
|
-
const isEpic = typeSelect.value === "epic";
|
|
150
|
-
parentField.hidden = isEpic;
|
|
151
|
-
if (isEpic) {
|
|
152
|
-
parentInput.value = "";
|
|
153
|
-
parentId.value = "";
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
typeSelect.addEventListener("change", syncParentVisibility);
|
|
157
|
-
syncParentVisibility();
|
|
158
|
-
|
|
159
|
-
form.addEventListener("submit", createTicketFromDrawer);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function createTicketFromDrawer(event) {
|
|
163
|
-
event.preventDefault();
|
|
164
|
-
const form = event.currentTarget;
|
|
165
|
-
const data = new FormData(form);
|
|
166
|
-
const labels = String(data.get("labels") || "")
|
|
167
|
-
.split(",")
|
|
168
|
-
.map((item) => item.trim())
|
|
169
|
-
.filter(Boolean);
|
|
170
|
-
|
|
171
|
-
const created = await api("/api/tickets", {
|
|
172
|
-
method: "POST",
|
|
173
|
-
body: {
|
|
174
|
-
board_id: state.boardId,
|
|
175
|
-
title: data.get("title"),
|
|
176
|
-
description: data.get("description"),
|
|
177
|
-
type: data.get("type"),
|
|
178
|
-
parent_ticket_id: data.get("parent_ticket_id") || null,
|
|
179
|
-
state_id: data.get("state"),
|
|
180
|
-
priority: Number(data.get("priority")),
|
|
181
|
-
labels
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
closeCreateFlyout();
|
|
186
|
-
state.selectedTicketId = created.id;
|
|
187
|
-
state.detailMode = "ticket";
|
|
188
|
-
await load();
|
|
189
|
-
syncUrlFromState();
|
|
190
|
-
}
|
package/public/js/dom.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Cached DOM references queried once at module-load time. Importing this
|
|
2
|
-
// module assumes the body has been parsed (the script tag is at the bottom
|
|
3
|
-
// of <body> and uses type="module", so this holds).
|
|
4
|
-
|
|
5
|
-
export const $ = (selector) => document.querySelector(selector);
|
|
6
|
-
|
|
7
|
-
export const boardMenuBtn = $("#boardMenuBtn");
|
|
8
|
-
export const boardFlyout = $("#boardFlyout");
|
|
9
|
-
export const kanban = $("#kanban");
|
|
10
|
-
export const drawer = $("#drawer");
|
|
11
|
-
export const drawerInner = $("#drawerInner");
|
|
12
|
-
export const drawerBackdrop = $("#drawerBackdrop");
|
|
13
|
-
export const createFlyout = $("#createFlyout");
|
|
14
|
-
export const createFlyoutInner = $("#createFlyoutInner");
|
|
15
|
-
export const createFlyoutBackdrop = $("#createFlyoutBackdrop");
|
|
16
|
-
export const searchInput = $("#searchInput");
|
|
17
|
-
export const searchResults = $("#searchResults");
|
|
18
|
-
export const themeToggle = $("#themeToggle");
|
|
19
|
-
export const themeIcon = $("#themeIcon");
|
package/public/js/drawer.js
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
// Right-side overlay drawer used for ticket detail and project settings.
|
|
2
|
-
// Owns the open/close transition state and the shared header layout
|
|
3
|
-
// (eyebrow, title, subtitle, tabs, archive/close buttons). Bodies are
|
|
4
|
-
// rendered by the feature modules that call `renderDrawerShell`.
|
|
5
|
-
|
|
6
|
-
import { drawer, drawerInner, drawerBackdrop } from "./dom.js";
|
|
7
|
-
import { state } from "./state.js";
|
|
8
|
-
import { escapeHtml } from "./format.js";
|
|
9
|
-
|
|
10
|
-
/** Bumps on each open/close so a stale `transitionend` from an old close cannot clear a newly opened drawer. */
|
|
11
|
-
let drawerToken = 0;
|
|
12
|
-
|
|
13
|
-
/** Aborts scroll/resize observers when tabs are re-rendered or the drawer closes. */
|
|
14
|
-
let drawerTabsScrollAbort = null;
|
|
15
|
-
|
|
16
|
-
/** Toggle edge-fade classes on the tab shell when the strip can scroll further left/right. */
|
|
17
|
-
function syncDrawerTabsScrollState(tabsEl) {
|
|
18
|
-
const shellEl = document.getElementById("drawerTabsShell");
|
|
19
|
-
if (!tabsEl || !shellEl) return;
|
|
20
|
-
|
|
21
|
-
const maxScroll = tabsEl.scrollWidth - tabsEl.clientWidth;
|
|
22
|
-
if (maxScroll <= 1) {
|
|
23
|
-
shellEl.classList.remove("can-scroll-left", "can-scroll-right");
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
shellEl.classList.toggle("can-scroll-left", tabsEl.scrollLeft > 1);
|
|
28
|
-
shellEl.classList.toggle("can-scroll-right", tabsEl.scrollLeft < maxScroll - 1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Keep the active settings tab visible when the strip overflows horizontally. */
|
|
32
|
-
function scrollActiveDrawerTabIntoView(tabsEl) {
|
|
33
|
-
tabsEl
|
|
34
|
-
?.querySelector(".drawer-tab.is-active")
|
|
35
|
-
?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function teardownDrawerTabsScroll() {
|
|
39
|
-
drawerTabsScrollAbort?.abort();
|
|
40
|
-
drawerTabsScrollAbort = null;
|
|
41
|
-
const shellEl = document.getElementById("drawerTabsShell");
|
|
42
|
-
const tabsEl = document.getElementById("drawerTabs");
|
|
43
|
-
shellEl?.classList.remove("can-scroll-left", "can-scroll-right");
|
|
44
|
-
tabsEl?.classList.remove("is-grabbing");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function bindDrawerTabsScroll(tabsEl) {
|
|
48
|
-
teardownDrawerTabsScroll();
|
|
49
|
-
if (!tabsEl) return;
|
|
50
|
-
|
|
51
|
-
const controller = new AbortController();
|
|
52
|
-
drawerTabsScrollAbort = controller;
|
|
53
|
-
const { signal } = controller;
|
|
54
|
-
|
|
55
|
-
const onScrollChange = () => syncDrawerTabsScrollState(tabsEl);
|
|
56
|
-
tabsEl.addEventListener("scroll", onScrollChange, { passive: true, signal });
|
|
57
|
-
|
|
58
|
-
if (typeof ResizeObserver !== "undefined") {
|
|
59
|
-
const observer = new ResizeObserver(onScrollChange);
|
|
60
|
-
observer.observe(tabsEl);
|
|
61
|
-
signal.addEventListener("abort", () => observer.disconnect(), { once: true });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Vertical wheel over the tab strip pans horizontally (no Shift required).
|
|
65
|
-
const LINE_SCROLL_PX = 16;
|
|
66
|
-
const wheelPixels = (value, mode) => {
|
|
67
|
-
if (mode === WheelEvent.DOM_DELTA_LINE) return value * LINE_SCROLL_PX;
|
|
68
|
-
if (mode === WheelEvent.DOM_DELTA_PAGE) return value * Math.max(tabsEl.clientWidth, 1);
|
|
69
|
-
return value;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
tabsEl.addEventListener(
|
|
73
|
-
"wheel",
|
|
74
|
-
(event) => {
|
|
75
|
-
if (event.defaultPrevented || event.ctrlKey) return;
|
|
76
|
-
if (tabsEl.scrollWidth <= tabsEl.clientWidth) return;
|
|
77
|
-
|
|
78
|
-
const horizontalDelta = event.deltaX || event.deltaY;
|
|
79
|
-
if (!horizontalDelta) return;
|
|
80
|
-
|
|
81
|
-
event.preventDefault();
|
|
82
|
-
tabsEl.scrollLeft += wheelPixels(horizontalDelta, event.deltaMode);
|
|
83
|
-
},
|
|
84
|
-
{ passive: false, signal }
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// Click-and-drag horizontal panning; threshold keeps tab clicks intact.
|
|
88
|
-
const DRAG_THRESHOLD = 4;
|
|
89
|
-
let dragActive = false;
|
|
90
|
-
let dragged = false;
|
|
91
|
-
let startX = 0;
|
|
92
|
-
let startScroll = 0;
|
|
93
|
-
let pointerId = 0;
|
|
94
|
-
|
|
95
|
-
tabsEl.addEventListener(
|
|
96
|
-
"pointerdown",
|
|
97
|
-
(event) => {
|
|
98
|
-
if (event.button !== 0) return;
|
|
99
|
-
if (tabsEl.scrollWidth <= tabsEl.clientWidth) return;
|
|
100
|
-
dragged = false;
|
|
101
|
-
dragActive = true;
|
|
102
|
-
pointerId = event.pointerId;
|
|
103
|
-
startX = event.clientX;
|
|
104
|
-
startScroll = tabsEl.scrollLeft;
|
|
105
|
-
},
|
|
106
|
-
{ signal }
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
tabsEl.addEventListener(
|
|
110
|
-
"pointermove",
|
|
111
|
-
(event) => {
|
|
112
|
-
if (!dragActive || event.pointerId !== pointerId) return;
|
|
113
|
-
const delta = event.clientX - startX;
|
|
114
|
-
if (!dragged && Math.abs(delta) > DRAG_THRESHOLD) {
|
|
115
|
-
dragged = true;
|
|
116
|
-
try {
|
|
117
|
-
tabsEl.setPointerCapture(pointerId);
|
|
118
|
-
} catch {
|
|
119
|
-
/* no-op: some browsers can't capture here */
|
|
120
|
-
}
|
|
121
|
-
tabsEl.classList.add("is-grabbing");
|
|
122
|
-
}
|
|
123
|
-
if (dragged) {
|
|
124
|
-
event.preventDefault();
|
|
125
|
-
tabsEl.scrollLeft = startScroll - delta;
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
{ signal }
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const endDrag = (event) => {
|
|
132
|
-
if (!dragActive || (event && event.pointerId !== pointerId)) return;
|
|
133
|
-
dragActive = false;
|
|
134
|
-
tabsEl.classList.remove("is-grabbing");
|
|
135
|
-
try {
|
|
136
|
-
tabsEl.releasePointerCapture(pointerId);
|
|
137
|
-
} catch {
|
|
138
|
-
/* no-op */
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
tabsEl.addEventListener("pointerup", endDrag, { signal });
|
|
143
|
-
tabsEl.addEventListener("pointercancel", endDrag, { signal });
|
|
144
|
-
tabsEl.addEventListener("pointerleave", endDrag, { signal });
|
|
145
|
-
|
|
146
|
-
tabsEl.addEventListener(
|
|
147
|
-
"click",
|
|
148
|
-
(event) => {
|
|
149
|
-
if (dragged) {
|
|
150
|
-
event.stopPropagation();
|
|
151
|
-
event.preventDefault();
|
|
152
|
-
dragged = false;
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
{ capture: true, signal }
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
requestAnimationFrame(() => {
|
|
159
|
-
if (signal.aborted) return;
|
|
160
|
-
onScrollChange();
|
|
161
|
-
scrollActiveDrawerTabIntoView(tabsEl);
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Slide the right drawer in (ticket detail, project settings, or create form). */
|
|
166
|
-
export function openDrawer() {
|
|
167
|
-
drawerToken += 1;
|
|
168
|
-
drawer.hidden = false;
|
|
169
|
-
drawerBackdrop.hidden = false;
|
|
170
|
-
requestAnimationFrame(() => {
|
|
171
|
-
drawer.classList.add("is-open");
|
|
172
|
-
drawerBackdrop.classList.add("is-visible");
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Close the drawer overlay. Uses transitionend with a timeout fallback for `hidden`.
|
|
178
|
-
* `drawerToken` invalidates pending finish callbacks if the user opens the drawer again mid-animation.
|
|
179
|
-
*/
|
|
180
|
-
export function closeDrawer() {
|
|
181
|
-
const finishToken = ++drawerToken;
|
|
182
|
-
const wasOpen = drawer.classList.contains("is-open");
|
|
183
|
-
drawer.classList.remove("is-open");
|
|
184
|
-
drawerBackdrop.classList.remove("is-visible");
|
|
185
|
-
|
|
186
|
-
const finish = () => {
|
|
187
|
-
if (drawerToken !== finishToken) return;
|
|
188
|
-
drawer.hidden = true;
|
|
189
|
-
drawerBackdrop.hidden = true;
|
|
190
|
-
drawerInner.innerHTML = "";
|
|
191
|
-
const titleBlockEl = document.getElementById("drawerTitleBlock");
|
|
192
|
-
const tabsEl = document.getElementById("drawerTabs");
|
|
193
|
-
const tabsShellEl = document.getElementById("drawerTabsShell");
|
|
194
|
-
if (titleBlockEl) titleBlockEl.innerHTML = "";
|
|
195
|
-
if (tabsEl) tabsEl.innerHTML = "";
|
|
196
|
-
if (tabsShellEl) tabsShellEl.hidden = true;
|
|
197
|
-
teardownDrawerTabsScroll();
|
|
198
|
-
drawer.classList.remove("is-wide");
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
if (!wasOpen) {
|
|
202
|
-
finish();
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let ended = false;
|
|
207
|
-
const end = () => {
|
|
208
|
-
if (ended) return;
|
|
209
|
-
ended = true;
|
|
210
|
-
finish();
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
drawer.addEventListener("transitionend", end, { once: true });
|
|
214
|
-
setTimeout(end, 360);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Fill the drawer's header + body slots. All drawer modes (ticket, project,
|
|
219
|
-
* create) should route through this so the header/title/close/tabs stay
|
|
220
|
-
* structurally consistent. `body` is an HTML string assigned to drawerInner.
|
|
221
|
-
*/
|
|
222
|
-
export function renderDrawerShell({ eyebrow, title, titleAttrs, subtitleHtml, tabs, activeTab, onTabSelect, body, mode }) {
|
|
223
|
-
drawer.classList.toggle("is-settings", mode === "settings");
|
|
224
|
-
drawer.classList.toggle("is-wide", mode === "ticket" || mode === "settings");
|
|
225
|
-
const titleBlockEl = document.getElementById("drawerTitleBlock");
|
|
226
|
-
const tabsEl = document.getElementById("drawerTabs");
|
|
227
|
-
const tabsShellEl = document.getElementById("drawerTabsShell");
|
|
228
|
-
|
|
229
|
-
// Rebuild the eyebrow / title / subtitle wholesale each render. Like
|
|
230
|
-
// drawerInner.innerHTML below, this means any listeners the caller attached
|
|
231
|
-
// last render (e.g. the inline title editor's click handler) die with the
|
|
232
|
-
// old nodes, instead of stacking on persistent shell elements.
|
|
233
|
-
const extraTitleClass = titleAttrs?.class ? ` ${titleAttrs.class}` : "";
|
|
234
|
-
let titleAttrHtml = ` class="drawer-title drawer-title-text${extraTitleClass}"`;
|
|
235
|
-
for (const [key, value] of Object.entries(titleAttrs || {})) {
|
|
236
|
-
if (key === "class") continue;
|
|
237
|
-
titleAttrHtml += ` ${key}="${escapeHtml(String(value))}"`;
|
|
238
|
-
}
|
|
239
|
-
const segments = [];
|
|
240
|
-
if (eyebrow) segments.push(`<span class="drawer-eyebrow">${escapeHtml(eyebrow)}</span>`);
|
|
241
|
-
if (title != null && String(title).trim() !== "") {
|
|
242
|
-
segments.push(`<h2${titleAttrHtml}>${escapeHtml(title)}</h2>`);
|
|
243
|
-
}
|
|
244
|
-
if (subtitleHtml) segments.push(`<div class="drawer-subtitle">${subtitleHtml}</div>`);
|
|
245
|
-
titleBlockEl.innerHTML = segments.join("");
|
|
246
|
-
|
|
247
|
-
if (tabs && tabs.length) {
|
|
248
|
-
if (tabsShellEl) tabsShellEl.hidden = false;
|
|
249
|
-
tabsEl.innerHTML = tabs
|
|
250
|
-
.map(
|
|
251
|
-
(t) =>
|
|
252
|
-
`<button type="button" role="tab" class="drawer-tab ${
|
|
253
|
-
t.id === activeTab ? "is-active" : ""
|
|
254
|
-
}" data-drawer-tab="${escapeHtml(t.id)}" aria-selected="${t.id === activeTab}">${escapeHtml(t.label)}</button>`
|
|
255
|
-
)
|
|
256
|
-
.join("");
|
|
257
|
-
if (typeof onTabSelect === "function") {
|
|
258
|
-
tabsEl.querySelectorAll(".drawer-tab").forEach((btn) => {
|
|
259
|
-
btn.addEventListener("click", () => onTabSelect(btn.dataset.drawerTab));
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
bindDrawerTabsScroll(tabsEl);
|
|
263
|
-
} else {
|
|
264
|
-
if (tabsShellEl) tabsShellEl.hidden = true;
|
|
265
|
-
tabsEl.innerHTML = "";
|
|
266
|
-
teardownDrawerTabsScroll();
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const archiveBtn = document.getElementById("drawerArchiveBtn");
|
|
270
|
-
if (archiveBtn) archiveBtn.hidden = state.detailMode !== "ticket";
|
|
271
|
-
|
|
272
|
-
drawerInner.innerHTML = body ?? "";
|
|
273
|
-
}
|