@starlove/orbit 1.4.1 → 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.4e6fbe20.js +0 -952
- package/dist/full/js/app.4e6fbe20.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 -230
- 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
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
export const FONT_STORAGE_KEY = "orbit_font_preferences";
|
|
2
|
-
export const FONT_CHANGE_EVENT = "orbit:font-preference-change";
|
|
3
|
-
|
|
4
|
-
// Inter is the default site font and for most categories; headings and badges
|
|
5
|
-
// default to Orbitron. The site font drives global font metrics such as size,
|
|
6
|
-
// spacing, and line-height.
|
|
7
|
-
export const DEFAULT_SITE_FONT = "inter";
|
|
8
|
-
export const DEFAULT_HEADING_FONT = "orbitron";
|
|
9
|
-
export const DEFAULT_BADGE_FONT = "orbitron";
|
|
10
|
-
export const DEFAULT_USER_SCALE = 1;
|
|
11
|
-
export const USER_SCALE_MIN = 0.85;
|
|
12
|
-
export const USER_SCALE_MAX = 1.3;
|
|
13
|
-
export const USER_SCALE_STEP = 0.05;
|
|
14
|
-
|
|
15
|
-
export const SITE_FONT_OPTIONS = Object.freeze([
|
|
16
|
-
"inter",
|
|
17
|
-
"openSans",
|
|
18
|
-
"jetBrainsMono",
|
|
19
|
-
"chakraPetch",
|
|
20
|
-
"orbitron",
|
|
21
|
-
"rajdhani",
|
|
22
|
-
"runescapeUF",
|
|
23
|
-
"openRing"
|
|
24
|
-
]);
|
|
25
|
-
|
|
26
|
-
export const FONT_OPTIONS = Object.freeze({
|
|
27
|
-
inter: {
|
|
28
|
-
label: "Inter",
|
|
29
|
-
stack: "\"Orbit Inter\", \"Inter\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
30
|
-
className: "font-inter"
|
|
31
|
-
},
|
|
32
|
-
openSans: {
|
|
33
|
-
label: "Open Sans",
|
|
34
|
-
stack: "\"Orbit Open Sans\", \"Open Sans\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
35
|
-
className: "font-open-sans"
|
|
36
|
-
},
|
|
37
|
-
jetBrainsMono: {
|
|
38
|
-
label: "JetBrains Mono",
|
|
39
|
-
stack: "\"Orbit JetBrains Mono\", \"JetBrains Mono\", \"Cascadia Mono\", \"SFMono-Regular\", Consolas, monospace",
|
|
40
|
-
className: "font-jetbrains-mono"
|
|
41
|
-
},
|
|
42
|
-
chakraPetch: {
|
|
43
|
-
label: "Chakra Petch",
|
|
44
|
-
stack: "\"Orbit Chakra Petch\", \"Chakra Petch\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
45
|
-
className: "font-chakra-petch"
|
|
46
|
-
},
|
|
47
|
-
orbitron: {
|
|
48
|
-
label: "Orbitron",
|
|
49
|
-
stack: "\"Orbit Orbitron\", \"Orbitron\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
50
|
-
className: "font-orbitron"
|
|
51
|
-
},
|
|
52
|
-
rajdhani: {
|
|
53
|
-
label: "Rajdhani",
|
|
54
|
-
stack: "\"Orbit Rajdhani\", \"Rajdhani\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
55
|
-
className: "font-rajdhani"
|
|
56
|
-
},
|
|
57
|
-
runescapeUF: {
|
|
58
|
-
label: "Runescape UF",
|
|
59
|
-
stack: "\"Orbit Runescape UF\", \"Cascadia Mono\", monospace",
|
|
60
|
-
className: "font-runescape-uf"
|
|
61
|
-
},
|
|
62
|
-
openRing: {
|
|
63
|
-
label: "Open Ring",
|
|
64
|
-
stack: "\"Orbit Open Ring\", \"Segoe UI Variable\", \"Segoe UI\", Arial, sans-serif",
|
|
65
|
-
className: "font-open-ring"
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Per-category overrides shown in the Appearance grid. Body text is not a
|
|
70
|
-
// target: it always follows the primary font (`site`), so it drives
|
|
71
|
-
// --orbit-font-body directly in applyFontPreferences instead.
|
|
72
|
-
export const FONT_TARGETS = Object.freeze({
|
|
73
|
-
heading: {
|
|
74
|
-
label: "Headings",
|
|
75
|
-
cssVar: "--orbit-font-heading",
|
|
76
|
-
dataAttr: "fontHeading"
|
|
77
|
-
},
|
|
78
|
-
control: {
|
|
79
|
-
label: "Controls",
|
|
80
|
-
cssVar: "--orbit-font-control",
|
|
81
|
-
dataAttr: "fontControl"
|
|
82
|
-
},
|
|
83
|
-
label: {
|
|
84
|
-
label: "Labels",
|
|
85
|
-
cssVar: "--orbit-font-label",
|
|
86
|
-
dataAttr: "fontLabel"
|
|
87
|
-
},
|
|
88
|
-
badge: {
|
|
89
|
-
label: "Badges",
|
|
90
|
-
cssVar: "--orbit-font-badge",
|
|
91
|
-
dataAttr: "fontBadge"
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
export const LEGACY_FONT_TARGET_ALIASES = Object.freeze({
|
|
96
|
-
ui: "control"
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
export const DEFAULT_FONT_TARGET_PREFERENCES = Object.freeze({
|
|
100
|
-
control: DEFAULT_SITE_FONT,
|
|
101
|
-
heading: DEFAULT_HEADING_FONT,
|
|
102
|
-
label: DEFAULT_SITE_FONT,
|
|
103
|
-
badge: DEFAULT_BADGE_FONT
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
export const DEFAULT_FONT_PREFERENCES = Object.freeze({
|
|
107
|
-
...DEFAULT_FONT_TARGET_PREFERENCES,
|
|
108
|
-
// Primary font: drives body text and the default for every override category.
|
|
109
|
-
site: DEFAULT_SITE_FONT,
|
|
110
|
-
// User size slider multiplier (drives --type-user-scale).
|
|
111
|
-
userScale: DEFAULT_USER_SCALE
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
function hasOwn(object, key) {
|
|
115
|
-
return Object.prototype.hasOwnProperty.call(object, key);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function clampUserScale(value) {
|
|
119
|
-
const number = Number(value);
|
|
120
|
-
if (!Number.isFinite(number)) return DEFAULT_USER_SCALE;
|
|
121
|
-
return Math.min(USER_SCALE_MAX, Math.max(USER_SCALE_MIN, number));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function normalizeFontPreferences(value) {
|
|
125
|
-
const input = value && typeof value === "object" ? value : {};
|
|
126
|
-
const normalized = { ...DEFAULT_FONT_PREFERENCES };
|
|
127
|
-
const explicitTargets = new Set();
|
|
128
|
-
for (const key of Object.keys(FONT_TARGETS)) {
|
|
129
|
-
if (typeof input[key] === "string" && hasOwn(FONT_OPTIONS, input[key])) {
|
|
130
|
-
normalized[key] = input[key];
|
|
131
|
-
explicitTargets.add(key);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const [legacyKey, target] of Object.entries(LEGACY_FONT_TARGET_ALIASES)) {
|
|
135
|
-
if (!explicitTargets.has(target) && typeof input[legacyKey] === "string" && hasOwn(FONT_OPTIONS, input[legacyKey])) {
|
|
136
|
-
normalized[target] = input[legacyKey];
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (typeof input.site === "string" && hasOwn(FONT_OPTIONS, input.site)) {
|
|
140
|
-
normalized.site = input.site;
|
|
141
|
-
}
|
|
142
|
-
if (input.userScale !== undefined) {
|
|
143
|
-
normalized.userScale = clampUserScale(input.userScale);
|
|
144
|
-
}
|
|
145
|
-
// Before the split, one "label" target drove both categories. Preserve a
|
|
146
|
-
// customized label choice for badges unless badge was saved separately.
|
|
147
|
-
if (!hasOwn(input, "badge") && hasOwn(input, "label") && input.label !== DEFAULT_SITE_FONT) {
|
|
148
|
-
normalized.badge = normalized.label;
|
|
149
|
-
}
|
|
150
|
-
return normalized;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function storedFontPreferences(storage = globalThis.localStorage) {
|
|
154
|
-
if (!storage) return { ...DEFAULT_FONT_PREFERENCES };
|
|
155
|
-
try {
|
|
156
|
-
const raw = storage.getItem(FONT_STORAGE_KEY);
|
|
157
|
-
if (!raw) return { ...DEFAULT_FONT_PREFERENCES };
|
|
158
|
-
return normalizeFontPreferences(JSON.parse(raw));
|
|
159
|
-
} catch {
|
|
160
|
-
return { ...DEFAULT_FONT_PREFERENCES };
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function fontStackFor(optionId) {
|
|
165
|
-
return FONT_OPTIONS[optionId]?.stack || FONT_OPTIONS[DEFAULT_SITE_FONT].stack;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function applyFontPreferences(preferences = storedFontPreferences(), { doc = globalThis.document } = {}) {
|
|
169
|
-
const normalized = normalizeFontPreferences(preferences);
|
|
170
|
-
const root = doc?.documentElement;
|
|
171
|
-
if (!root) return normalized;
|
|
172
|
-
|
|
173
|
-
for (const [target, config] of Object.entries(FONT_TARGETS)) {
|
|
174
|
-
const optionId = normalized[target];
|
|
175
|
-
root.style?.setProperty?.(config.cssVar, fontStackFor(optionId));
|
|
176
|
-
root.setAttribute?.(`data-${config.dataAttr.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`, optionId);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Body text (paragraphs, card titles, long-form prose, editors) always tracks
|
|
180
|
-
// the primary font rather than being an independent category.
|
|
181
|
-
root.style?.setProperty?.("--orbit-font-body", fontStackFor(normalized.site));
|
|
182
|
-
// The only size lever is the Appearance slider; font family no longer changes size.
|
|
183
|
-
root.style?.setProperty?.("--type-user-scale", String(normalized.userScale));
|
|
184
|
-
root.setAttribute?.("data-font-site", normalized.site);
|
|
185
|
-
return normalized;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function setFontPreferences(preferences, {
|
|
189
|
-
doc = globalThis.document,
|
|
190
|
-
storage = globalThis.localStorage,
|
|
191
|
-
eventTarget = globalThis.window
|
|
192
|
-
} = {}) {
|
|
193
|
-
const normalized = normalizeFontPreferences(preferences);
|
|
194
|
-
storage?.setItem?.(FONT_STORAGE_KEY, JSON.stringify(normalized));
|
|
195
|
-
applyFontPreferences(normalized, { doc });
|
|
196
|
-
eventTarget?.dispatchEvent?.(new CustomEvent(FONT_CHANGE_EVENT, { detail: { preferences: normalized } }));
|
|
197
|
-
return normalized;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function resetFontPreferences(options = {}) {
|
|
201
|
-
options.storage?.removeItem?.(FONT_STORAGE_KEY);
|
|
202
|
-
return setFontPreferences(DEFAULT_FONT_PREFERENCES, options);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function setFontPreference(target, optionId, options = {}) {
|
|
206
|
-
if (!hasOwn(FONT_TARGETS, target)) throw new Error(`Unknown font target: ${target}`);
|
|
207
|
-
if (!hasOwn(FONT_OPTIONS, optionId)) throw new Error(`Unknown font option: ${optionId}`);
|
|
208
|
-
return setFontPreferences({ ...storedFontPreferences(options.storage), [target]: optionId }, options);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Swap the whole site to a primary font: sets every target plus the `site`
|
|
212
|
-
// driver. Per-target overrides can be applied afterward via setFontPreference.
|
|
213
|
-
export function setSiteFont(optionId, options = {}) {
|
|
214
|
-
if (!hasOwn(FONT_OPTIONS, optionId)) throw new Error(`Unknown font option: ${optionId}`);
|
|
215
|
-
const next = { ...storedFontPreferences(options.storage) };
|
|
216
|
-
for (const target of Object.keys(FONT_TARGETS)) next[target] = optionId;
|
|
217
|
-
next.site = optionId;
|
|
218
|
-
return setFontPreferences(next, options);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function setUserScale(value, options = {}) {
|
|
222
|
-
return setFontPreferences(
|
|
223
|
-
{ ...storedFontPreferences(options.storage), userScale: clampUserScale(value) },
|
|
224
|
-
options
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function applyStoredFontPreferences(options = {}) {
|
|
229
|
-
return applyFontPreferences(storedFontPreferences(options.storage), options);
|
|
230
|
-
}
|
package/public/js/format.js
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
// Pure string helpers: escape, dates, ticket/type/priority labels.
|
|
2
|
-
// Zero side effects, no imports — safe to import from anywhere.
|
|
3
|
-
|
|
4
|
-
export function escapeHtml(value) {
|
|
5
|
-
return String(value ?? "")
|
|
6
|
-
.replaceAll("&", "&")
|
|
7
|
-
.replaceAll("<", "<")
|
|
8
|
-
.replaceAll(">", ">")
|
|
9
|
-
.replaceAll('"', """)
|
|
10
|
-
.replaceAll("'", "'");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// UTF-8 bytes mis-decoded as CP-437 (the typical Windows-tmux artifact).
|
|
14
|
-
// Ported from a local fix-newlines utility — every entry is [mojibake sequence,
|
|
15
|
-
// what it should have been]. Long sequences first so shorter prefixes do not
|
|
16
|
-
// eat them.
|
|
17
|
-
const MOJIBAKE_REPLACEMENTS = [
|
|
18
|
-
// General Punctuation (UTF-8 E2 80 XX → CP-437 "ΓÇ" + glyph)
|
|
19
|
-
["ΓÇô", "–"],
|
|
20
|
-
["ΓÇö", "—"],
|
|
21
|
-
["ΓÇ£", '"'],
|
|
22
|
-
["ΓÇ¥", '"'],
|
|
23
|
-
["ΓÇÿ", "'"],
|
|
24
|
-
["ΓÇÖ", "'"],
|
|
25
|
-
["ΓǪ", "…"],
|
|
26
|
-
["ΓÇó", "•"],
|
|
27
|
-
["ΓÇ░", "‰"],
|
|
28
|
-
["ΓÇ╣", "‹"],
|
|
29
|
-
["ΓÇ║", "›"],
|
|
30
|
-
|
|
31
|
-
// Box drawing — light (UTF-8 E2 94 XX → CP-437 "Γö" + glyph)
|
|
32
|
-
["ΓöÇ", "─"],
|
|
33
|
-
["Γöé", "│"],
|
|
34
|
-
["Γöî", "┌"],
|
|
35
|
-
["ΓöÉ", "┐"],
|
|
36
|
-
["Γöö", "└"],
|
|
37
|
-
["Γöÿ", "┘"],
|
|
38
|
-
["Γö£", "├"],
|
|
39
|
-
["Γöñ", "┤"],
|
|
40
|
-
["Γö¼", "┬"],
|
|
41
|
-
["Γö┤", "┴"],
|
|
42
|
-
["Γö╝", "┼"],
|
|
43
|
-
|
|
44
|
-
// Box drawing — heavy/double + rounded corners (UTF-8 E2 95 XX → "Γò" + glyph)
|
|
45
|
-
["ΓòÉ", "═"],
|
|
46
|
-
["Γòæ", "║"],
|
|
47
|
-
["ΓòÆ", "╒"],
|
|
48
|
-
["Γòö", "╔"],
|
|
49
|
-
["Γò¡", "╭"],
|
|
50
|
-
["Γò«", "╮"],
|
|
51
|
-
["Γò»", "╯"],
|
|
52
|
-
["Γò░", "╰"],
|
|
53
|
-
|
|
54
|
-
// Geometric shapes (UTF-8 E2 96 XX / E2 97 XX → "Γû" / "Γù" + glyph)
|
|
55
|
-
["Γûæ", "░"],
|
|
56
|
-
["ΓûÆ", "▒"],
|
|
57
|
-
["Γûô", "▓"],
|
|
58
|
-
["ΓûÉ", "▐"],
|
|
59
|
-
["Γûî", "▌"],
|
|
60
|
-
["ΓûÄ", ">"],
|
|
61
|
-
["Γûá", "■"],
|
|
62
|
-
["Γû╝", "▼"],
|
|
63
|
-
["ΓùÅ", "●"],
|
|
64
|
-
["Γùï", "○"],
|
|
65
|
-
|
|
66
|
-
// Misc symbols (UTF-8 E2 9A XX → "ΓÜ" + glyph)
|
|
67
|
-
["ΓÜò", "⚕"],
|
|
68
|
-
["ΓÜá", "⚠"],
|
|
69
|
-
["ΓÜí", "⚡"],
|
|
70
|
-
["ΓÜô", "⚓"],
|
|
71
|
-
|
|
72
|
-
// Arrows (UTF-8 E2 86 XX → "Γå" + glyph)
|
|
73
|
-
["ΓåÉ", "←"],
|
|
74
|
-
["Γåæ", "↑"],
|
|
75
|
-
["ΓåÆ", "→"],
|
|
76
|
-
["Γåô", "↓"],
|
|
77
|
-
["Γåö", "↔"],
|
|
78
|
-
|
|
79
|
-
// Latin-1 Supplement (UTF-8 C2 XX → CP-437 "┬" + glyph)
|
|
80
|
-
[" ", " "],
|
|
81
|
-
["┬⌐", "©"],
|
|
82
|
-
["┬«", "®"],
|
|
83
|
-
["┬░", "°"],
|
|
84
|
-
["┬º", "§"],
|
|
85
|
-
["┬╖", "·"],
|
|
86
|
-
|
|
87
|
-
// Stray curly quotes that survived as proper Unicode; normalize to ASCII
|
|
88
|
-
// straight quotes so pasted terminal/log text stays consistent.
|
|
89
|
-
["“", '"'],
|
|
90
|
-
["”", '"'],
|
|
91
|
-
["‘", "'"],
|
|
92
|
-
["’", "'"],
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
function fixMojibake(text) {
|
|
96
|
-
if (!text) return text;
|
|
97
|
-
let out = text;
|
|
98
|
-
for (const [from, to] of MOJIBAKE_REPLACEMENTS) {
|
|
99
|
-
out = out.split(from).join(to);
|
|
100
|
-
}
|
|
101
|
-
return out;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Repair common mojibake and normalize line endings without disturbing the
|
|
106
|
-
* author's indentation or soft line breaks. Use this on inbound text before
|
|
107
|
-
* saving (so the DB stays clean going forward) and again before rendering (so
|
|
108
|
-
* existing dirty rows display fixed without a migration).
|
|
109
|
-
*/
|
|
110
|
-
export function cleanText(value) {
|
|
111
|
-
if (!value) return "";
|
|
112
|
-
return fixMojibake(String(value)).replace(/\r\n?/g, "\n");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function renderPreservedText(value) {
|
|
116
|
-
return escapeHtml(cleanText(value));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const SAFE_LINK_PROTOCOLS = new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
120
|
-
|
|
121
|
-
function sanitizeMarkdownUrl(value) {
|
|
122
|
-
const href = String(value || "").trim();
|
|
123
|
-
if (!href) return "";
|
|
124
|
-
|
|
125
|
-
// Collapse control characters/whitespace before checking the scheme so inputs
|
|
126
|
-
// like `java\nscript:` or `jav\tascript:` cannot bypass the blocklist.
|
|
127
|
-
const compact = href.replace(/[\u0000-\u001f\u007f\s]+/g, "").toLowerCase();
|
|
128
|
-
if (/^(?:javascript|data|vbscript):/.test(compact)) return "";
|
|
129
|
-
|
|
130
|
-
const scheme = compact.match(/^([a-z][a-z0-9+.-]*):/);
|
|
131
|
-
if (scheme && !SAFE_LINK_PROTOCOLS.has(`${scheme[1]}:`)) return "";
|
|
132
|
-
|
|
133
|
-
return href;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function renderEmphasis(text) {
|
|
137
|
-
let html = escapeHtml(text);
|
|
138
|
-
html = html.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>");
|
|
139
|
-
html = html.replace(/(\*|_)(?=\S)([^*_]*?\S)\1/g, "<em>$2</em>");
|
|
140
|
-
return html;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function renderLinksAndEmphasis(text) {
|
|
144
|
-
const linkPattern = /\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
145
|
-
let html = "";
|
|
146
|
-
let lastIndex = 0;
|
|
147
|
-
let match;
|
|
148
|
-
|
|
149
|
-
while ((match = linkPattern.exec(text))) {
|
|
150
|
-
html += renderEmphasis(text.slice(lastIndex, match.index));
|
|
151
|
-
|
|
152
|
-
const labelHtml = renderEmphasis(match[1]);
|
|
153
|
-
const href = sanitizeMarkdownUrl(match[2]);
|
|
154
|
-
html += href
|
|
155
|
-
? `<a href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${labelHtml}</a>`
|
|
156
|
-
: labelHtml;
|
|
157
|
-
|
|
158
|
-
lastIndex = match.index + match[0].length;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
html += renderEmphasis(text.slice(lastIndex));
|
|
162
|
-
return html;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function renderInlineMarkdown(text) {
|
|
166
|
-
const source = String(text ?? "");
|
|
167
|
-
let html = "";
|
|
168
|
-
let cursor = 0;
|
|
169
|
-
|
|
170
|
-
while (cursor < source.length) {
|
|
171
|
-
const codeStart = source.indexOf("`", cursor);
|
|
172
|
-
if (codeStart === -1) {
|
|
173
|
-
html += renderLinksAndEmphasis(source.slice(cursor));
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const codeEnd = source.indexOf("`", codeStart + 1);
|
|
178
|
-
if (codeEnd === -1) {
|
|
179
|
-
html += renderLinksAndEmphasis(source.slice(cursor));
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
html += renderLinksAndEmphasis(source.slice(cursor, codeStart));
|
|
184
|
-
html += `<code>${escapeHtml(source.slice(codeStart + 1, codeEnd))}</code>`;
|
|
185
|
-
cursor = codeEnd + 1;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return html;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function renderParagraph(lines) {
|
|
192
|
-
return `<p>${renderInlineMarkdown(lines.join("\n")).replaceAll("\n", "<br>")}</p>`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function renderTaskItem(rawContent) {
|
|
196
|
-
// GFM-style `- [ ] text` or `- [x] text`. We render as a disabled checkbox
|
|
197
|
-
// followed by the inline-rendered remainder. The list itself gets a
|
|
198
|
-
// `task-list` class so CSS can drop the default bullet.
|
|
199
|
-
const match = rawContent.match(/^\[( |x|X)\]\s+(.*)$/);
|
|
200
|
-
if (!match) return null;
|
|
201
|
-
const checked = match[1].toLowerCase() === "x";
|
|
202
|
-
const body = renderInlineMarkdown(match[2].trim()).replaceAll("\n", "<br>");
|
|
203
|
-
return {
|
|
204
|
-
checked,
|
|
205
|
-
html: `<input type="checkbox" disabled${checked ? " checked" : ""}> ${body}`,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export function renderMarkdown(value) {
|
|
210
|
-
// Mojibake repair runs at render time too so existing dirty rows display
|
|
211
|
-
// cleanly without a backfill. cleanText also normalizes line endings.
|
|
212
|
-
const source = cleanText(value);
|
|
213
|
-
if (!source.trim()) return "";
|
|
214
|
-
|
|
215
|
-
const blocks = [];
|
|
216
|
-
const lines = source.split("\n");
|
|
217
|
-
let paragraph = [];
|
|
218
|
-
let list = null;
|
|
219
|
-
let blockquote = null;
|
|
220
|
-
|
|
221
|
-
const flushParagraph = () => {
|
|
222
|
-
if (!paragraph.length) return;
|
|
223
|
-
blocks.push(renderParagraph(paragraph));
|
|
224
|
-
paragraph = [];
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const flushList = () => {
|
|
228
|
-
if (!list) return;
|
|
229
|
-
const classAttr = list.taskList ? ' class="task-list"' : "";
|
|
230
|
-
blocks.push(
|
|
231
|
-
`<${list.type}${classAttr}>${list.items
|
|
232
|
-
.map((item) => `<li${item.taskClass ? ` class="${item.taskClass}"` : ""}>${item.html}</li>`)
|
|
233
|
-
.join("")}</${list.type}>`
|
|
234
|
-
);
|
|
235
|
-
list = null;
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
const flushBlockquote = () => {
|
|
239
|
-
if (!blockquote) return;
|
|
240
|
-
blocks.push(`<blockquote>${renderMarkdown(blockquote.join("\n"))}</blockquote>`);
|
|
241
|
-
blockquote = null;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const flushAll = () => {
|
|
245
|
-
flushParagraph();
|
|
246
|
-
flushList();
|
|
247
|
-
flushBlockquote();
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
251
|
-
const line = lines[i];
|
|
252
|
-
|
|
253
|
-
if (/^\s*```/.test(line)) {
|
|
254
|
-
flushAll();
|
|
255
|
-
const codeLines = [];
|
|
256
|
-
i += 1;
|
|
257
|
-
while (i < lines.length && !/^\s*```\s*$/.test(lines[i])) {
|
|
258
|
-
codeLines.push(lines[i]);
|
|
259
|
-
i += 1;
|
|
260
|
-
}
|
|
261
|
-
blocks.push(`<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Horizontal rule: a line of three+ dashes / asterisks / underscores.
|
|
266
|
-
if (/^\s*(?:-\s*){3,}\s*$/.test(line) || /^\s*(?:\*\s*){3,}\s*$/.test(line) || /^\s*(?:_\s*){3,}\s*$/.test(line)) {
|
|
267
|
-
flushAll();
|
|
268
|
-
blocks.push("<hr>");
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ATX header: 1-6 # followed by a space and content.
|
|
273
|
-
const header = line.match(/^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
274
|
-
if (header) {
|
|
275
|
-
flushAll();
|
|
276
|
-
const level = header[1].length;
|
|
277
|
-
blocks.push(`<h${level}>${renderInlineMarkdown(header[2])}</h${level}>`);
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Blockquote: `> ...`. We collect consecutive `>` lines, strip the leading
|
|
282
|
-
// marker, and recurse so quoted content can itself contain lists/headers.
|
|
283
|
-
const quoteMatch = line.match(/^\s{0,3}>\s?(.*)$/);
|
|
284
|
-
if (quoteMatch) {
|
|
285
|
-
flushParagraph();
|
|
286
|
-
flushList();
|
|
287
|
-
if (!blockquote) blockquote = [];
|
|
288
|
-
blockquote.push(quoteMatch[1]);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (blockquote && !line.trim()) {
|
|
292
|
-
flushBlockquote();
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
flushBlockquote();
|
|
296
|
-
|
|
297
|
-
if (!line.trim()) {
|
|
298
|
-
flushParagraph();
|
|
299
|
-
flushList();
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const ordered = line.match(/^\s*\d+[.)]\s+(.+)$/);
|
|
304
|
-
const unordered = line.match(/^\s*[-*+]\s+(.+)$/);
|
|
305
|
-
if (ordered || unordered) {
|
|
306
|
-
flushParagraph();
|
|
307
|
-
const type = ordered ? "ol" : "ul";
|
|
308
|
-
const content = (ordered || unordered)[1].trim();
|
|
309
|
-
|
|
310
|
-
let task = null;
|
|
311
|
-
if (unordered) task = renderTaskItem(content);
|
|
312
|
-
|
|
313
|
-
if (!list || list.type !== type || Boolean(list.taskList) !== Boolean(task)) {
|
|
314
|
-
flushList();
|
|
315
|
-
list = { type, items: [], taskList: Boolean(task) };
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (task) {
|
|
319
|
-
list.items.push({
|
|
320
|
-
html: task.html,
|
|
321
|
-
taskClass: task.checked ? "task-item task-item-done" : "task-item",
|
|
322
|
-
});
|
|
323
|
-
} else {
|
|
324
|
-
list.items.push({
|
|
325
|
-
html: renderInlineMarkdown(content).replaceAll("\n", "<br>"),
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
flushList();
|
|
332
|
-
paragraph.push(line);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
flushAll();
|
|
336
|
-
|
|
337
|
-
return blocks.join("");
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
export function formatDate(value) {
|
|
341
|
-
if (!value) return "";
|
|
342
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
343
|
-
month: "short",
|
|
344
|
-
day: "numeric",
|
|
345
|
-
hour: "numeric",
|
|
346
|
-
minute: "2-digit"
|
|
347
|
-
}).format(new Date(value));
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/** Richer timestamp for the detail pane “Updated” line (refreshes after every save). */
|
|
351
|
-
export function formatDateDetail(value) {
|
|
352
|
-
if (!value) return "";
|
|
353
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
354
|
-
year: "numeric",
|
|
355
|
-
month: "short",
|
|
356
|
-
day: "numeric",
|
|
357
|
-
hour: "numeric",
|
|
358
|
-
minute: "2-digit",
|
|
359
|
-
second: "2-digit"
|
|
360
|
-
}).format(new Date(value));
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
export function ticketLabel(ticket) {
|
|
364
|
-
return `#${ticket.number}`;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export function stateClassFor(ticketOrState) {
|
|
368
|
-
const source =
|
|
369
|
-
ticketOrState?.state_role || ticketOrState?.role || ticketOrState?.state_name || ticketOrState?.name || "state";
|
|
370
|
-
return String(source)
|
|
371
|
-
.toLowerCase()
|
|
372
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
373
|
-
.replace(/^-|-$/g, "") || "state";
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export function canonicalTicketType(type) {
|
|
377
|
-
if (type === "epic" || type === "feature" || type === "bug" || type === "task") return type;
|
|
378
|
-
return "task";
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Short label for dense surfaces (card pills, child-card pills).
|
|
382
|
-
export function typeLabel(type) {
|
|
383
|
-
const labels = {
|
|
384
|
-
epic: "Epic",
|
|
385
|
-
feature: "Feature",
|
|
386
|
-
task: "Task",
|
|
387
|
-
bug: "Bug"
|
|
388
|
-
};
|
|
389
|
-
return labels[canonicalTicketType(type)] || "Task";
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Long, hierarchy-aware label for dropdowns where we have the room.
|
|
393
|
-
export function typeLabelLong(type) {
|
|
394
|
-
const labels = {
|
|
395
|
-
epic: "Epic",
|
|
396
|
-
feature: "Feature",
|
|
397
|
-
task: "Task",
|
|
398
|
-
bug: "Bug"
|
|
399
|
-
};
|
|
400
|
-
return labels[canonicalTicketType(type)] || "Task";
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export function priorityLabel(priority) {
|
|
404
|
-
const labels = {
|
|
405
|
-
0: "MAYBE",
|
|
406
|
-
1: "LOW",
|
|
407
|
-
2: "MED",
|
|
408
|
-
3: "HIGH",
|
|
409
|
-
4: "URGENT"
|
|
410
|
-
};
|
|
411
|
-
return labels[Number(priority)] || "MED";
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** Lowercase key used for CSS class targeting: `priority-maybe`, `priority-urgent`, etc. */
|
|
415
|
-
export function priorityKeyFor(priority) {
|
|
416
|
-
const keys = ["maybe", "low", "med", "high", "urgent"];
|
|
417
|
-
const n = Number(priority);
|
|
418
|
-
return keys[Number.isFinite(n) ? Math.max(0, Math.min(4, n)) : 2];
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export function renderLabels(labels = []) {
|
|
422
|
-
if (!labels.length) return "";
|
|
423
|
-
return `
|
|
424
|
-
<div class="labels">
|
|
425
|
-
${labels
|
|
426
|
-
.map(
|
|
427
|
-
(label) =>
|
|
428
|
-
`<span class="label" style="--label-color: ${escapeHtml(label.color)}">${escapeHtml(label.name)}</span>`
|
|
429
|
-
)
|
|
430
|
-
.join("")}
|
|
431
|
-
</div>
|
|
432
|
-
`;
|
|
433
|
-
}
|
package/public/js/help-menu.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Help & About dialog. The topbar "Help" button opens a centered
|
|
2
|
-
// .orbit-dialog (same shell as the welcome dialog) describing what Orbit is,
|
|
3
|
-
// how to get started, and where the docs live. modal.js owns the
|
|
4
|
-
// Escape/backdrop/close-button behavior; this module only wires the open
|
|
5
|
-
// trigger and best-effort fills the version pill from the build's version.json.
|
|
6
|
-
|
|
7
|
-
import { openModal, wireModal } from "./modal.js";
|
|
8
|
-
|
|
9
|
-
async function fillVersion(pill) {
|
|
10
|
-
if (!pill) return;
|
|
11
|
-
try {
|
|
12
|
-
const res = await fetch("./version.json", { cache: "no-store" });
|
|
13
|
-
if (!res.ok) return;
|
|
14
|
-
const info = await res.json();
|
|
15
|
-
if (info?.version) pill.textContent = `v${info.version}`;
|
|
16
|
-
} catch {
|
|
17
|
-
// Dev edition serves raw modules without version.json — leave the
|
|
18
|
-
// static "Orbit" label in place.
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function wireHelpMenu() {
|
|
23
|
-
const dialog = document.getElementById("helpDialog");
|
|
24
|
-
const helpBtn = document.getElementById("helpBtn");
|
|
25
|
-
if (!dialog || !helpBtn) return;
|
|
26
|
-
|
|
27
|
-
wireModal(dialog);
|
|
28
|
-
fillVersion(document.getElementById("helpDialogVersion"));
|
|
29
|
-
|
|
30
|
-
helpBtn.addEventListener("click", () => openModal(dialog));
|
|
31
|
-
}
|