@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.
Files changed (329) hide show
  1. package/Dockerfile +10 -8
  2. package/README.md +146 -164
  3. package/dist/full/csp.txt +1 -0
  4. package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
  5. package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
  6. package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
  7. package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
  8. package/dist/full/fonts/Mona_Sans/README.txt +10 -0
  9. package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
  10. package/dist/full/fonts/Newsreader/README.txt +8 -0
  11. package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
  12. package/dist/full/index.html +36 -653
  13. package/dist/full/js/app.BmuponPJ.js +32 -0
  14. package/dist/full/js/app.BmuponPJ.js.map +1 -0
  15. package/dist/full/js/chunk.1fJGQGbW.js +2 -0
  16. package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
  17. package/dist/full/js/chunk.5D7JO2zp.js +2 -0
  18. package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
  19. package/dist/full/js/chunk.BrNn_D79.js +2 -0
  20. package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
  21. package/dist/full/js/chunk.CokJXIqM.js +2 -0
  22. package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
  23. package/dist/full/js/chunk.D_Haqy6R.js +2 -0
  24. package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
  25. package/dist/full/js/chunk.RO0jsH6I.js +15 -0
  26. package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
  27. package/dist/full/js/chunk.dqWOEtBM.js +2 -0
  28. package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
  29. package/dist/full/js/warm.f260bbbc.js +15 -0
  30. package/dist/full/orbits-favicon.png +0 -0
  31. package/dist/full/orbits-logo-bg-wide.png +0 -0
  32. package/dist/full/orbits-logo-bg.png +0 -0
  33. package/dist/full/orbits-logo.png +0 -0
  34. package/dist/full/styles.2b154ffe.css +1 -0
  35. package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
  36. package/dist/full/version.json +2 -2
  37. package/docs/AGENT_GUIDE.md +114 -0
  38. package/docs/UPDATING.md +249 -64
  39. package/lib/adapters/indexeddb/blobs.d.ts +3 -0
  40. package/lib/adapters/indexeddb/blobs.js +54 -0
  41. package/lib/adapters/indexeddb/database.d.ts +60 -0
  42. package/lib/adapters/indexeddb/database.js +126 -0
  43. package/lib/adapters/indexeddb/index.d.ts +5 -0
  44. package/lib/adapters/indexeddb/index.js +3 -0
  45. package/lib/adapters/indexeddb/locks.d.ts +14 -0
  46. package/lib/adapters/indexeddb/locks.js +40 -0
  47. package/lib/adapters/indexeddb/store.d.ts +40 -0
  48. package/lib/adapters/indexeddb/store.js +424 -0
  49. package/lib/adapters/indexeddb/tables.d.ts +43 -0
  50. package/lib/adapters/indexeddb/tables.js +127 -0
  51. package/lib/adapters/memory/blobs.d.ts +3 -0
  52. package/lib/adapters/memory/blobs.js +44 -0
  53. package/lib/adapters/memory/board.d.ts +41 -0
  54. package/lib/adapters/memory/board.js +521 -0
  55. package/lib/adapters/memory/index.d.ts +2 -0
  56. package/lib/adapters/memory/index.js +2 -0
  57. package/lib/adapters/memory/search.d.ts +6 -0
  58. package/lib/adapters/memory/search.js +30 -0
  59. package/lib/adapters/memory/store.d.ts +34 -0
  60. package/lib/adapters/memory/store.js +266 -0
  61. package/lib/adapters/sqlite/backups.d.ts +53 -0
  62. package/lib/adapters/sqlite/backups.js +232 -0
  63. package/lib/adapters/sqlite/blobs.d.ts +14 -0
  64. package/lib/adapters/sqlite/blobs.js +109 -0
  65. package/lib/adapters/sqlite/board.d.ts +77 -0
  66. package/lib/adapters/sqlite/board.js +328 -0
  67. package/lib/adapters/sqlite/database.d.ts +67 -0
  68. package/lib/adapters/sqlite/database.js +285 -0
  69. package/lib/adapters/sqlite/index.d.ts +3 -0
  70. package/lib/adapters/sqlite/index.js +2 -0
  71. package/lib/adapters/sqlite/lock.d.ts +11 -0
  72. package/lib/adapters/sqlite/lock.js +34 -0
  73. package/lib/adapters/sqlite/records.d.ts +54 -0
  74. package/lib/adapters/sqlite/records.js +546 -0
  75. package/lib/adapters/sqlite/schema.d.ts +54 -0
  76. package/lib/adapters/sqlite/schema.js +276 -0
  77. package/lib/adapters/sqlite/store.d.ts +39 -0
  78. package/lib/adapters/sqlite/store.js +385 -0
  79. package/lib/browser/in-page.d.ts +31 -0
  80. package/lib/browser/in-page.js +46 -0
  81. package/lib/browser/old-preview.d.ts +13 -0
  82. package/lib/browser/old-preview.js +80 -0
  83. package/lib/browser/preview-board.d.ts +8 -0
  84. package/lib/browser/preview-board.js +69 -0
  85. package/lib/cli/main.d.ts +7 -0
  86. package/lib/cli/main.js +343 -0
  87. package/lib/cli/orbit.d.ts +8 -0
  88. package/lib/cli/orbit.js +25 -0
  89. package/lib/client/http.d.ts +38 -0
  90. package/lib/client/http.js +334 -0
  91. package/lib/client/local.d.ts +26 -0
  92. package/lib/client/local.js +190 -0
  93. package/lib/client/object-url.d.ts +8 -0
  94. package/lib/client/object-url.js +21 -0
  95. package/lib/client/types.d.ts +268 -0
  96. package/lib/client/types.js +8 -0
  97. package/lib/commands/agent-tools.d.ts +31 -0
  98. package/lib/commands/agent-tools.js +100 -0
  99. package/lib/commands/index.d.ts +56 -0
  100. package/lib/commands/index.js +298 -0
  101. package/lib/commands/parse.d.ts +73 -0
  102. package/lib/commands/parse.js +231 -0
  103. package/lib/commands/present.d.ts +134 -0
  104. package/lib/commands/present.js +198 -0
  105. package/lib/commands/session.d.ts +85 -0
  106. package/lib/commands/session.js +138 -0
  107. package/lib/commands/verbs.d.ts +35 -0
  108. package/lib/commands/verbs.js +842 -0
  109. package/lib/core/actors.d.ts +18 -0
  110. package/lib/core/actors.js +45 -0
  111. package/lib/core/agent.d.ts +128 -0
  112. package/lib/core/agent.js +323 -0
  113. package/lib/core/boards.d.ts +113 -0
  114. package/lib/core/boards.js +288 -0
  115. package/lib/core/bootstrap.d.ts +47 -0
  116. package/lib/core/bootstrap.js +100 -0
  117. package/lib/core/bytes.d.ts +10 -0
  118. package/lib/core/bytes.js +52 -0
  119. package/lib/core/card-paths.d.ts +25 -0
  120. package/lib/core/card-paths.js +44 -0
  121. package/lib/core/context.d.ts +60 -0
  122. package/lib/core/context.js +42 -0
  123. package/lib/core/cross-board.d.ts +81 -0
  124. package/lib/core/cross-board.js +228 -0
  125. package/lib/core/describe.d.ts +21 -0
  126. package/lib/core/describe.js +81 -0
  127. package/lib/core/errors.d.ts +159 -0
  128. package/lib/core/errors.js +160 -0
  129. package/lib/core/files.d.ts +79 -0
  130. package/lib/core/files.js +262 -0
  131. package/lib/core/history.d.ts +20 -0
  132. package/lib/core/history.js +53 -0
  133. package/lib/core/image-types.d.ts +16 -0
  134. package/lib/core/image-types.js +36 -0
  135. package/lib/core/model.d.ts +263 -0
  136. package/lib/core/model.js +34 -0
  137. package/lib/core/nebula-link.d.ts +14 -0
  138. package/lib/core/nebula-link.js +39 -0
  139. package/lib/core/relations.d.ts +58 -0
  140. package/lib/core/relations.js +112 -0
  141. package/lib/core/search.d.ts +38 -0
  142. package/lib/core/search.js +92 -0
  143. package/lib/core/sessions.d.ts +44 -0
  144. package/lib/core/sessions.js +158 -0
  145. package/lib/core/snapshot-schema.d.ts +295 -0
  146. package/lib/core/snapshot-schema.js +82 -0
  147. package/lib/core/snapshots.d.ts +75 -0
  148. package/lib/core/snapshots.js +409 -0
  149. package/lib/core/states.d.ts +25 -0
  150. package/lib/core/states.js +126 -0
  151. package/lib/core/text.d.ts +40 -0
  152. package/lib/core/text.js +113 -0
  153. package/lib/core/tickets.d.ts +69 -0
  154. package/lib/core/tickets.js +311 -0
  155. package/lib/core/trello.d.ts +17 -0
  156. package/lib/core/trello.js +269 -0
  157. package/lib/core/verdicts.d.ts +28 -0
  158. package/lib/core/verdicts.js +92 -0
  159. package/lib/core/views.d.ts +95 -0
  160. package/lib/core/views.js +186 -0
  161. package/lib/http/app.d.ts +46 -0
  162. package/lib/http/app.js +491 -0
  163. package/lib/mcp-server.d.ts +10 -0
  164. package/lib/mcp-server.js +120 -0
  165. package/lib/node/assets.d.ts +7 -0
  166. package/lib/node/assets.js +68 -0
  167. package/lib/node/data-folder.d.ts +21 -0
  168. package/lib/node/data-folder.js +34 -0
  169. package/lib/node/env.d.ts +38 -0
  170. package/lib/node/env.js +49 -0
  171. package/lib/node/host-check.d.ts +24 -0
  172. package/lib/node/host-check.js +53 -0
  173. package/lib/node/host-services.d.ts +9 -0
  174. package/lib/node/host-services.js +150 -0
  175. package/lib/node/local.d.ts +22 -0
  176. package/lib/node/local.js +61 -0
  177. package/lib/node/migrate.d.ts +53 -0
  178. package/lib/node/migrate.js +635 -0
  179. package/lib/node/package.d.ts +7 -0
  180. package/lib/node/package.js +14 -0
  181. package/lib/node/repo-board.d.ts +49 -0
  182. package/lib/node/repo-board.js +245 -0
  183. package/lib/node/serve.d.ts +8 -0
  184. package/lib/node/serve.js +106 -0
  185. package/lib/orbit.d.ts +47 -0
  186. package/lib/orbit.js +44 -0
  187. package/lib/quiet-sqlite-warning.d.ts +1 -0
  188. package/lib/quiet-sqlite-warning.js +18 -0
  189. package/lib/store/blob-path.d.ts +6 -0
  190. package/lib/store/blob-path.js +12 -0
  191. package/lib/store/conformance.d.ts +52 -0
  192. package/lib/store/conformance.js +1449 -0
  193. package/lib/store/search-words.d.ts +10 -0
  194. package/lib/store/search-words.js +19 -0
  195. package/lib/store/types.d.ts +452 -0
  196. package/lib/store/types.js +34 -0
  197. package/llms.txt +63 -0
  198. package/package.json +109 -25
  199. package/src/cli/orbit.js +11 -658
  200. package/src/mcp-server.js +16 -876
  201. package/SKILL-ORBIT.md +0 -151
  202. package/build.mjs +0 -210
  203. package/dist/full/js/app.4e6fbe20.js +0 -952
  204. package/dist/full/js/app.4e6fbe20.js.map +0 -7
  205. package/dist/full/styles.4e60d824.css +0 -1
  206. package/docs/AGENT_PROTOCOL.md +0 -424
  207. package/docs/MCP_SETUP.md +0 -54
  208. package/docs/ORBIT_DISPATCH.md +0 -280
  209. package/docs/SS-Orbit-Dark.png +0 -0
  210. package/docs/SS-Orbit-Light.png +0 -0
  211. package/docs/mcp-client.example.json +0 -13
  212. package/docs/setup/orbit-init-wizard.md +0 -174
  213. package/public/apple-touch-icon.png +0 -0
  214. package/public/cursors/live_arrow.cur +0 -0
  215. package/public/cursors/live_link.cur +0 -0
  216. package/public/cursors/live_move.cur +0 -0
  217. package/public/cursors/live_select.cur +0 -0
  218. package/public/cursors/live_unavail.cur +0 -0
  219. package/public/favicon-32x32.png +0 -0
  220. package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
  221. package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
  222. package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
  223. package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
  224. package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
  225. package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
  226. package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
  227. package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
  228. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
  229. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
  230. package/public/fonts/Chakra_Petch/OFL.txt +0 -93
  231. package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
  232. package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
  233. package/public/fonts/Inter/OFL.txt +0 -93
  234. package/public/fonts/Inter/README.txt +0 -118
  235. package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  236. package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
  237. package/public/fonts/JetBrains_Mono/README.txt +0 -79
  238. package/public/fonts/OpenRing-Regular.ttf +0 -0
  239. package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  240. package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  241. package/public/fonts/Open_Sans/README.txt +0 -100
  242. package/public/fonts/Orbitron/OFL.txt +0 -93
  243. package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
  244. package/public/fonts/Orbitron/README.txt +0 -68
  245. package/public/fonts/Rajdhani/OFL.txt +0 -93
  246. package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
  247. package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
  248. package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
  249. package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
  250. package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
  251. package/public/fonts/runescape-uf.ttf +0 -0
  252. package/public/index.html +0 -769
  253. package/public/js/actor-labels.js +0 -65
  254. package/public/js/api.js +0 -39
  255. package/public/js/app.js +0 -45
  256. package/public/js/artifact-drawer.js +0 -79
  257. package/public/js/board-export.js +0 -161
  258. package/public/js/board-menu.js +0 -374
  259. package/public/js/card-actions.js +0 -545
  260. package/public/js/config.js +0 -20
  261. package/public/js/create-card.js +0 -190
  262. package/public/js/dom.js +0 -19
  263. package/public/js/drawer.js +0 -273
  264. package/public/js/font-preference.js +0 -230
  265. package/public/js/format.js +0 -433
  266. package/public/js/help-menu.js +0 -31
  267. package/public/js/icons.js +0 -16
  268. package/public/js/kanban.js +0 -694
  269. package/public/js/local-backend.js +0 -1015
  270. package/public/js/main.js +0 -161
  271. package/public/js/modal.js +0 -33
  272. package/public/js/motion-preference.js +0 -64
  273. package/public/js/panel.js +0 -58
  274. package/public/js/router.js +0 -134
  275. package/public/js/search.js +0 -56
  276. package/public/js/settings-tabs.js +0 -7
  277. package/public/js/settings.js +0 -1634
  278. package/public/js/sse.js +0 -159
  279. package/public/js/starscape.js +0 -332
  280. package/public/js/state.js +0 -129
  281. package/public/js/text-editing.js +0 -128
  282. package/public/js/theme-preference.js +0 -95
  283. package/public/js/ticket-detail.js +0 -1516
  284. package/public/js/ticket-typeahead.js +0 -49
  285. package/public/js/toast.js +0 -89
  286. package/public/js/topbar-notes.js +0 -155
  287. package/public/js/transport.js +0 -22
  288. package/public/js/typeahead.js +0 -206
  289. package/public/js/unread.js +0 -121
  290. package/public/js/update-check.js +0 -58
  291. package/public/js/url-routes.js +0 -94
  292. package/public/orbits-favicon.png +0 -0
  293. package/public/orbits-logo-bg-wide.png +0 -0
  294. package/public/orbits-logo-bg.png +0 -0
  295. package/public/orbits-logo.png +0 -0
  296. package/public/styles.css +0 -7709
  297. package/src/core/agent.js +0 -387
  298. package/src/core/agents-md.js +0 -109
  299. package/src/core/artifacts.js +0 -149
  300. package/src/core/attachments.js +0 -238
  301. package/src/core/auth.js +0 -85
  302. package/src/core/backups.js +0 -142
  303. package/src/core/board.js +0 -148
  304. package/src/core/boards.js +0 -409
  305. package/src/core/bootstrap.js +0 -170
  306. package/src/core/css-bundle.js +0 -30
  307. package/src/core/db.js +0 -315
  308. package/src/core/delete-board.js +0 -163
  309. package/src/core/dispatch.js +0 -1007
  310. package/src/core/events.js +0 -314
  311. package/src/core/http.js +0 -115
  312. package/src/core/migrate.js +0 -198
  313. package/src/core/paths.js +0 -55
  314. package/src/core/provision-repo-board.js +0 -119
  315. package/src/core/queries.js +0 -246
  316. package/src/core/registry.js +0 -211
  317. package/src/core/relations.js +0 -163
  318. package/src/core/review-verdicts.js +0 -138
  319. package/src/core/router.js +0 -556
  320. package/src/core/search.js +0 -183
  321. package/src/core/seed.js +0 -234
  322. package/src/core/snapshots.js +0 -692
  323. package/src/core/states.js +0 -167
  324. package/src/core/system-picker.js +0 -109
  325. package/src/core/tickets.js +0 -400
  326. package/src/core/util.js +0 -106
  327. package/src/core/version-check.js +0 -154
  328. package/src/mcp/orbit-client.js +0 -287
  329. 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
- }
@@ -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("<", "&lt;")
8
- .replaceAll(">", "&gt;")
9
- .replaceAll('"', "&quot;")
10
- .replaceAll("'", "&#039;");
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
- }
@@ -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
- }