devglide 0.1.1

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 (252) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -0
  3. package/bin/claude-md-template.js +94 -0
  4. package/bin/devglide.js +387 -0
  5. package/package.json +85 -0
  6. package/pnpm-workspace.yaml +3 -0
  7. package/src/apps/coder/.turbo/turbo-lint.log +5 -0
  8. package/src/apps/coder/package.json +16 -0
  9. package/src/apps/coder/public/favicon.svg +7 -0
  10. package/src/apps/coder/public/page.css +275 -0
  11. package/src/apps/coder/public/page.js +528 -0
  12. package/src/apps/coder/server.js +3 -0
  13. package/src/apps/documentation/public/page.css +597 -0
  14. package/src/apps/documentation/public/page.js +609 -0
  15. package/src/apps/kanban/.turbo/turbo-lint.log +97 -0
  16. package/src/apps/kanban/.turbo/turbo-typecheck.log +5 -0
  17. package/src/apps/kanban/package.json +32 -0
  18. package/src/apps/kanban/public/favicon.svg +7 -0
  19. package/src/apps/kanban/public/page.css +1010 -0
  20. package/src/apps/kanban/public/page.js +1730 -0
  21. package/src/apps/kanban/public/vendor/marked.min.js +6 -0
  22. package/src/apps/kanban/public/vendor/sortable.min.js +2 -0
  23. package/src/apps/kanban/src/db.ts +319 -0
  24. package/src/apps/kanban/src/index.ts +14 -0
  25. package/src/apps/kanban/src/mcp-helpers.test.ts +88 -0
  26. package/src/apps/kanban/src/mcp-helpers.ts +60 -0
  27. package/src/apps/kanban/src/mcp.ts +59 -0
  28. package/src/apps/kanban/src/routes/attachments.ts +161 -0
  29. package/src/apps/kanban/src/routes/features.ts +233 -0
  30. package/src/apps/kanban/src/routes/issues.ts +373 -0
  31. package/src/apps/kanban/src/tools/feature-tools.ts +164 -0
  32. package/src/apps/kanban/src/tools/item-tools.ts +307 -0
  33. package/src/apps/kanban/src/tools/versioned-entry-tools.ts +72 -0
  34. package/src/apps/kanban/tsconfig.check.json +9 -0
  35. package/src/apps/kanban/tsconfig.json +9 -0
  36. package/src/apps/keymap/.turbo/turbo-lint.log +5 -0
  37. package/src/apps/keymap/package.json +16 -0
  38. package/src/apps/keymap/public/page.css +275 -0
  39. package/src/apps/keymap/public/page.js +294 -0
  40. package/src/apps/keymap/server.js +25 -0
  41. package/src/apps/log/.turbo/turbo-build.log +5 -0
  42. package/src/apps/log/.turbo/turbo-lint.log +45 -0
  43. package/src/apps/log/.turbo/turbo-typecheck.log +5 -0
  44. package/src/apps/log/node_modules/.bin/tsc +21 -0
  45. package/src/apps/log/node_modules/.bin/tsserver +21 -0
  46. package/src/apps/log/node_modules/.bin/tsx +21 -0
  47. package/src/apps/log/package.json +36 -0
  48. package/src/apps/log/public/console-sniffer.js +221 -0
  49. package/src/apps/log/public/favicon.svg +7 -0
  50. package/src/apps/log/public/page.css +322 -0
  51. package/src/apps/log/public/page.js +463 -0
  52. package/src/apps/log/src/index.ts +9 -0
  53. package/src/apps/log/src/mcp.ts +122 -0
  54. package/src/apps/log/src/routes/log.ts +333 -0
  55. package/src/apps/log/src/routes/status.ts +25 -0
  56. package/src/apps/log/src/server-sniffer.ts +118 -0
  57. package/src/apps/log/src/services/file-patterns.ts +39 -0
  58. package/src/apps/log/src/services/file-tailer.ts +228 -0
  59. package/src/apps/log/src/services/line-parser.ts +94 -0
  60. package/src/apps/log/src/services/log-writer.ts +39 -0
  61. package/src/apps/log/tsconfig.json +8 -0
  62. package/src/apps/prompts/.turbo/turbo-build.log +5 -0
  63. package/src/apps/prompts/.turbo/turbo-lint.log +24 -0
  64. package/src/apps/prompts/.turbo/turbo-typecheck.log +5 -0
  65. package/src/apps/prompts/mcp.ts +175 -0
  66. package/src/apps/prompts/node_modules/.bin/tsc +21 -0
  67. package/src/apps/prompts/node_modules/.bin/tsserver +21 -0
  68. package/src/apps/prompts/node_modules/.bin/tsx +21 -0
  69. package/src/apps/prompts/package.json +25 -0
  70. package/src/apps/prompts/public/page.css +315 -0
  71. package/src/apps/prompts/public/page.js +541 -0
  72. package/src/apps/prompts/services/prompt-store.ts +212 -0
  73. package/src/apps/prompts/src/index.ts +9 -0
  74. package/src/apps/prompts/tsconfig.json +8 -0
  75. package/src/apps/prompts/types.ts +27 -0
  76. package/src/apps/shell/.turbo/turbo-build.log +5 -0
  77. package/src/apps/shell/.turbo/turbo-lint.log +34 -0
  78. package/src/apps/shell/.turbo/turbo-typecheck.log +5 -0
  79. package/src/apps/shell/package.json +35 -0
  80. package/src/apps/shell/public/favicon.svg +7 -0
  81. package/src/apps/shell/public/page.css +407 -0
  82. package/src/apps/shell/public/page.js +1577 -0
  83. package/src/apps/shell/src/index.ts +150 -0
  84. package/src/apps/shell/src/mcp.ts +398 -0
  85. package/src/apps/shell/src/shell-types.ts +41 -0
  86. package/src/apps/shell/tsconfig.json +8 -0
  87. package/src/apps/test/.turbo/turbo-build.log +5 -0
  88. package/src/apps/test/.turbo/turbo-lint.log +27 -0
  89. package/src/apps/test/.turbo/turbo-typecheck.log +5 -0
  90. package/src/apps/test/node_modules/.bin/tsc +21 -0
  91. package/src/apps/test/node_modules/.bin/tsserver +21 -0
  92. package/src/apps/test/node_modules/.bin/tsx +21 -0
  93. package/src/apps/test/node_modules/.bin/uuid +21 -0
  94. package/src/apps/test/package.json +35 -0
  95. package/src/apps/test/public/favicon.svg +7 -0
  96. package/src/apps/test/public/page.css +499 -0
  97. package/src/apps/test/public/page.js +417 -0
  98. package/src/apps/test/public/scenario-runner.js +450 -0
  99. package/src/apps/test/src/index.ts +9 -0
  100. package/src/apps/test/src/mcp.ts +192 -0
  101. package/src/apps/test/src/routes/trigger.ts +285 -0
  102. package/src/apps/test/src/services/scenario-broadcaster.ts +60 -0
  103. package/src/apps/test/src/services/scenario-manager.ts +361 -0
  104. package/src/apps/test/src/services/scenario-store.ts +145 -0
  105. package/src/apps/test/tsconfig.json +8 -0
  106. package/src/apps/vocabulary/.turbo/turbo-build.log +5 -0
  107. package/src/apps/vocabulary/.turbo/turbo-lint.log +25 -0
  108. package/src/apps/vocabulary/.turbo/turbo-typecheck.log +5 -0
  109. package/src/apps/vocabulary/mcp.ts +173 -0
  110. package/src/apps/vocabulary/node_modules/.bin/tsc +21 -0
  111. package/src/apps/vocabulary/node_modules/.bin/tsserver +21 -0
  112. package/src/apps/vocabulary/node_modules/.bin/tsx +21 -0
  113. package/src/apps/vocabulary/package.json +25 -0
  114. package/src/apps/vocabulary/public/page.css +247 -0
  115. package/src/apps/vocabulary/public/page.js +444 -0
  116. package/src/apps/vocabulary/services/vocabulary-store.ts +179 -0
  117. package/src/apps/vocabulary/src/index.ts +10 -0
  118. package/src/apps/vocabulary/tsconfig.json +8 -0
  119. package/src/apps/vocabulary/types.ts +22 -0
  120. package/src/apps/voice/.turbo/turbo-build.log +5 -0
  121. package/src/apps/voice/.turbo/turbo-lint.log +43 -0
  122. package/src/apps/voice/.turbo/turbo-typecheck.log +5 -0
  123. package/src/apps/voice/node_modules/.bin/openai +21 -0
  124. package/src/apps/voice/node_modules/.bin/tsc +21 -0
  125. package/src/apps/voice/node_modules/.bin/tsserver +21 -0
  126. package/src/apps/voice/node_modules/.bin/tsx +21 -0
  127. package/src/apps/voice/package.json +35 -0
  128. package/src/apps/voice/public/favicon.svg +7 -0
  129. package/src/apps/voice/public/page.css +388 -0
  130. package/src/apps/voice/public/page.js +718 -0
  131. package/src/apps/voice/src/index.ts +10 -0
  132. package/src/apps/voice/src/mcp.ts +70 -0
  133. package/src/apps/voice/src/providers/index.ts +85 -0
  134. package/src/apps/voice/src/providers/openai-compatible.ts +94 -0
  135. package/src/apps/voice/src/providers/types.ts +27 -0
  136. package/src/apps/voice/src/routes/config.ts +118 -0
  137. package/src/apps/voice/src/routes/transcribe.ts +90 -0
  138. package/src/apps/voice/src/services/config-store.ts +129 -0
  139. package/src/apps/voice/src/services/stats.ts +108 -0
  140. package/src/apps/voice/src/transcribe.ts +11 -0
  141. package/src/apps/voice/src/utils/mime.ts +16 -0
  142. package/src/apps/voice/tsconfig.json +8 -0
  143. package/src/apps/workflow/.turbo/turbo-build.log +5 -0
  144. package/src/apps/workflow/.turbo/turbo-lint.log +96 -0
  145. package/src/apps/workflow/.turbo/turbo-typecheck.log +5 -0
  146. package/src/apps/workflow/engine/executors/decision-executor.ts +87 -0
  147. package/src/apps/workflow/engine/executors/file-executor.ts +90 -0
  148. package/src/apps/workflow/engine/executors/git-executor.ts +137 -0
  149. package/src/apps/workflow/engine/executors/http-executor.ts +65 -0
  150. package/src/apps/workflow/engine/executors/index.ts +28 -0
  151. package/src/apps/workflow/engine/executors/kanban-executor.ts +154 -0
  152. package/src/apps/workflow/engine/executors/llm-executor.ts +46 -0
  153. package/src/apps/workflow/engine/executors/log-executor.ts +62 -0
  154. package/src/apps/workflow/engine/executors/loop-executor.ts +14 -0
  155. package/src/apps/workflow/engine/executors/shell-executor.ts +107 -0
  156. package/src/apps/workflow/engine/executors/sub-workflow-executor.ts +61 -0
  157. package/src/apps/workflow/engine/executors/test-executor.ts +73 -0
  158. package/src/apps/workflow/engine/executors/trigger-executor.ts +39 -0
  159. package/src/apps/workflow/engine/expression-evaluator.ts +117 -0
  160. package/src/apps/workflow/engine/graph-runner.ts +438 -0
  161. package/src/apps/workflow/engine/node-executor.ts +104 -0
  162. package/src/apps/workflow/engine/node-registry.ts +15 -0
  163. package/src/apps/workflow/engine/variable-resolver.ts +109 -0
  164. package/src/apps/workflow/mcp.ts +223 -0
  165. package/src/apps/workflow/node_modules/.bin/tsc +21 -0
  166. package/src/apps/workflow/node_modules/.bin/tsserver +21 -0
  167. package/src/apps/workflow/node_modules/.bin/tsx +21 -0
  168. package/src/apps/workflow/package.json +25 -0
  169. package/src/apps/workflow/public/editor/canvas.js +366 -0
  170. package/src/apps/workflow/public/editor/drag-manager.js +326 -0
  171. package/src/apps/workflow/public/editor/edge-renderer.js +235 -0
  172. package/src/apps/workflow/public/editor/history-manager.js +147 -0
  173. package/src/apps/workflow/public/editor/layout-engine.js +159 -0
  174. package/src/apps/workflow/public/editor/node-renderer.js +199 -0
  175. package/src/apps/workflow/public/editor/selection-manager.js +193 -0
  176. package/src/apps/workflow/public/favicon.svg +7 -0
  177. package/src/apps/workflow/public/models/node-types.js +300 -0
  178. package/src/apps/workflow/public/models/workflow-model.js +257 -0
  179. package/src/apps/workflow/public/page.css +406 -0
  180. package/src/apps/workflow/public/page.js +658 -0
  181. package/src/apps/workflow/public/panels/inspector.js +360 -0
  182. package/src/apps/workflow/public/panels/palette.js +106 -0
  183. package/src/apps/workflow/public/panels/run-view.js +275 -0
  184. package/src/apps/workflow/public/panels/toolbar.js +232 -0
  185. package/src/apps/workflow/public/panels/workflow-list.js +237 -0
  186. package/src/apps/workflow/public/state/store.js +47 -0
  187. package/src/apps/workflow/services/custom-node-loader.ts +48 -0
  188. package/src/apps/workflow/services/legacy-converter.ts +72 -0
  189. package/src/apps/workflow/services/run-manager.ts +190 -0
  190. package/src/apps/workflow/services/workflow-store.ts +424 -0
  191. package/src/apps/workflow/services/workflow-validator.test.ts +103 -0
  192. package/src/apps/workflow/services/workflow-validator.ts +98 -0
  193. package/src/apps/workflow/src/index.ts +10 -0
  194. package/src/apps/workflow/templates/ci-pipeline.json +18 -0
  195. package/src/apps/workflow/templates/code-review.json +22 -0
  196. package/src/apps/workflow/templates/kanban-testing.json +24 -0
  197. package/src/apps/workflow/tsconfig.json +8 -0
  198. package/src/apps/workflow/types.ts +268 -0
  199. package/src/packages/auth-middleware.ts +14 -0
  200. package/src/packages/design-tokens/.turbo/turbo-build.log +10 -0
  201. package/src/packages/design-tokens/STYLEGUIDE.md +414 -0
  202. package/src/packages/design-tokens/build.js +413 -0
  203. package/src/packages/design-tokens/demo/index.html +1367 -0
  204. package/src/packages/design-tokens/demo/proposition-a.html +717 -0
  205. package/src/packages/design-tokens/demo/proposition-b.html +1239 -0
  206. package/src/packages/design-tokens/demo/proposition-c.html +1049 -0
  207. package/src/packages/design-tokens/dist/tailwind-preset.js +115 -0
  208. package/src/packages/design-tokens/dist/tokens.css +345 -0
  209. package/src/packages/design-tokens/dist/tokens.d.ts +229 -0
  210. package/src/packages/design-tokens/dist/tokens.js +386 -0
  211. package/src/packages/design-tokens/package.json +25 -0
  212. package/src/packages/design-tokens/tokens.json +228 -0
  213. package/src/packages/devtools-middleware.ts +22 -0
  214. package/src/packages/eslint-config/index.js +63 -0
  215. package/src/packages/eslint-config/node_modules/.bin/eslint +21 -0
  216. package/src/packages/eslint-config/package.json +18 -0
  217. package/src/packages/json-file-store.ts +232 -0
  218. package/src/packages/mcp-utils/.turbo/turbo-build.log +5 -0
  219. package/src/packages/mcp-utils/dist/index.d.ts +33 -0
  220. package/src/packages/mcp-utils/dist/index.d.ts.map +1 -0
  221. package/src/packages/mcp-utils/dist/index.js +126 -0
  222. package/src/packages/mcp-utils/dist/index.js.map +1 -0
  223. package/src/packages/mcp-utils/node_modules/.bin/tsc +21 -0
  224. package/src/packages/mcp-utils/node_modules/.bin/tsserver +21 -0
  225. package/src/packages/mcp-utils/package.json +32 -0
  226. package/src/packages/mcp-utils/src/index.ts +171 -0
  227. package/src/packages/mcp-utils/tsconfig.json +9 -0
  228. package/src/packages/paths.ts +18 -0
  229. package/src/packages/project-context/index.js +55 -0
  230. package/src/packages/project-context/package.json +13 -0
  231. package/src/packages/project-store.ts +127 -0
  232. package/src/packages/server-sniffer.ts +132 -0
  233. package/src/packages/shared-assets/favicon.svg +7 -0
  234. package/src/packages/shared-assets/keymap-registry.js +512 -0
  235. package/src/packages/shared-assets/logo.svg +6 -0
  236. package/src/packages/shared-assets/package.json +11 -0
  237. package/src/packages/shared-assets/ui-utils.js +48 -0
  238. package/src/packages/shared-assets/voice-widget.d.ts +37 -0
  239. package/src/packages/shared-assets/voice-widget.js +695 -0
  240. package/src/packages/shared-types/.turbo/turbo-build.log +5 -0
  241. package/src/packages/shared-types/dist/index.d.ts +39 -0
  242. package/src/packages/shared-types/dist/index.d.ts.map +1 -0
  243. package/src/packages/shared-types/node_modules/.bin/tsc +21 -0
  244. package/src/packages/shared-types/node_modules/.bin/tsserver +21 -0
  245. package/src/packages/shared-types/package.json +25 -0
  246. package/src/packages/shared-types/src/index.ts +41 -0
  247. package/src/packages/shared-types/tsconfig.json +11 -0
  248. package/src/packages/tsconfig/base.json +15 -0
  249. package/src/packages/tsconfig/next.json +14 -0
  250. package/src/packages/tsconfig/node.json +11 -0
  251. package/src/packages/tsconfig/package.json +10 -0
  252. package/turbo.json +25 -0
@@ -0,0 +1,512 @@
1
+ /**
2
+ * DevGlide Keymap Registry
3
+ *
4
+ * Global, platform-aware keymap registry loaded via <script> tag.
5
+ * Manages default bindings, user overrides (persisted to localStorage),
6
+ * and provides matching / formatting utilities for keyboard shortcuts.
7
+ */
8
+ (function () {
9
+ 'use strict';
10
+
11
+ var STORAGE_KEY = 'devglide:keymap';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Platform detection
15
+ // ---------------------------------------------------------------------------
16
+
17
+ var isMac = typeof navigator !== 'undefined' &&
18
+ /Mac|iPod|iPhone|iPad/.test(navigator.platform);
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Internal state
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** @type {Map<string, {defaultBinding: object, description: string, group: string}>} */
25
+ var actions = new Map();
26
+
27
+ /** @type {Map<string, object>} user overrides keyed by actionId */
28
+ var overrides = new Map();
29
+
30
+ /** @type {Set<Function>} change listeners */
31
+ var listeners = new Set();
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Persistence helpers
35
+ // ---------------------------------------------------------------------------
36
+
37
+ function loadOverrides() {
38
+ try {
39
+ var raw = localStorage.getItem(STORAGE_KEY);
40
+ if (raw) {
41
+ var parsed = JSON.parse(raw);
42
+ if (parsed && typeof parsed === 'object') {
43
+ Object.keys(parsed).forEach(function (id) {
44
+ overrides.set(id, parsed[id]);
45
+ });
46
+ }
47
+ }
48
+ } catch (_) { /* ignore corrupt data */ }
49
+ }
50
+
51
+ function saveOverrides() {
52
+ try {
53
+ var obj = {};
54
+ overrides.forEach(function (binding, id) {
55
+ obj[id] = binding;
56
+ });
57
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(obj));
58
+ } catch (_) { /* storage full / unavailable */ }
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Notify helpers
63
+ // ---------------------------------------------------------------------------
64
+
65
+ function notifyChange(actionId) {
66
+ listeners.forEach(function (cb) {
67
+ try { cb(actionId); } catch (_) { /* swallow listener errors */ }
68
+ });
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Modifier resolution helpers
73
+ // ---------------------------------------------------------------------------
74
+
75
+ /**
76
+ * Evaluate whether a modifier flag matches the event.
77
+ * Handles the special 'ctrlOrMeta' value.
78
+ */
79
+ function modifierMatches(bindingValue, eventCtrl, eventMeta) {
80
+ if (bindingValue === true) return true;
81
+ if (bindingValue === 'ctrlOrMeta') {
82
+ return isMac ? eventMeta : eventCtrl;
83
+ }
84
+ return false;
85
+ }
86
+
87
+ function eventHasCtrlOrMeta(e) {
88
+ return isMac ? e.metaKey : e.ctrlKey;
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Matching helpers
93
+ // ---------------------------------------------------------------------------
94
+
95
+ var MODIFIER_KEYS = new Set([
96
+ 'Control', 'Alt', 'Shift', 'Meta',
97
+ 'ControlLeft', 'ControlRight',
98
+ 'AltLeft', 'AltRight',
99
+ 'ShiftLeft', 'ShiftRight',
100
+ 'MetaLeft', 'MetaRight',
101
+ ]);
102
+
103
+ function isModifierKey(event) {
104
+ return MODIFIER_KEYS.has(event.key) || MODIFIER_KEYS.has(event.code);
105
+ }
106
+
107
+ /**
108
+ * Check if a binding specifies a modifier-only combo (no key/code).
109
+ */
110
+ function isModifierOnly(binding) {
111
+ return !binding.key && !binding.code;
112
+ }
113
+
114
+ /**
115
+ * Check if a binding requires no modifiers at all.
116
+ */
117
+ function hasNoModifiers(binding) {
118
+ return !binding.ctrlKey && !binding.altKey && !binding.shiftKey &&
119
+ !binding.ctrlOrMeta && binding.ctrlOrMeta !== 'ctrlOrMeta' &&
120
+ binding.ctrlKey !== 'ctrlOrMeta';
121
+ }
122
+
123
+ /**
124
+ * Return true if `event` satisfies `binding`.
125
+ */
126
+ function bindingMatchesEvent(binding, event) {
127
+ // --- Modifier checks ---
128
+
129
+ // ctrlKey on binding
130
+ var expectCtrl = binding.ctrlKey;
131
+ var expectCtrlOrMeta = (binding.ctrlOrMeta === true) ||
132
+ (binding.ctrlKey === 'ctrlOrMeta') ||
133
+ (binding.ctrlOrMeta === 'ctrlOrMeta');
134
+
135
+ if (expectCtrlOrMeta) {
136
+ // On Mac: require metaKey, on Win/Linux: require ctrlKey
137
+ if (isMac) {
138
+ if (!event.metaKey) return false;
139
+ } else {
140
+ if (!event.ctrlKey) return false;
141
+ }
142
+ } else if (expectCtrl === true) {
143
+ if (!event.ctrlKey) return false;
144
+ } else {
145
+ // No ctrl expected — but we must ensure the user isn't pressing ctrl
146
+ // (unless ctrlOrMeta accounts for it on the platform)
147
+ if (event.ctrlKey && !isMac) return false;
148
+ // On Mac, ctrlKey can be physical Control which we might want to ignore
149
+ // but for clean matching, if no ctrl is expected, reject if ctrl pressed
150
+ if (event.ctrlKey && isMac) {
151
+ // Allow ctrlKey on Mac if metaKey is what ctrlOrMeta maps to.
152
+ // Actually, if we're here, there's no ctrlOrMeta, so reject.
153
+ return false;
154
+ }
155
+ }
156
+
157
+ // altKey
158
+ if (binding.altKey) {
159
+ if (!event.altKey) return false;
160
+ } else {
161
+ if (event.altKey) return false;
162
+ }
163
+
164
+ // shiftKey
165
+ if (binding.shiftKey) {
166
+ if (!event.shiftKey) return false;
167
+ } else {
168
+ if (event.shiftKey) return false;
169
+ }
170
+
171
+ // metaKey: if ctrlOrMeta is active and we're on Mac, we already checked
172
+ // metaKey above. Otherwise, meta should not be pressed (unless on Mac
173
+ // where ctrlOrMeta covers it).
174
+ if (!expectCtrlOrMeta) {
175
+ if (isMac && event.metaKey) return false;
176
+ if (!isMac && event.metaKey) return false;
177
+ } else {
178
+ // On the non-matching platform side, the other modifier should not be
179
+ // pressed. E.g. on Mac, ctrlOrMeta -> metaKey, so ctrlKey should be
180
+ // false (unless it happens to also be pressed, which we'll be lenient
181
+ // about — actually let's be strict for clean matching).
182
+ // We already validated the primary; no extra check needed since we
183
+ // checked ctrlKey above for non-ctrlOrMeta case.
184
+ }
185
+
186
+ // --- Key / Code checks ---
187
+
188
+ if (isModifierOnly(binding)) {
189
+ // Modifier-only binding: match when the event key is itself a modifier.
190
+ return isModifierKey(event);
191
+ }
192
+
193
+ if (binding.code) {
194
+ if (event.code !== binding.code) return false;
195
+ }
196
+
197
+ if (binding.key) {
198
+ if (event.key !== binding.key) return false;
199
+ }
200
+
201
+ return true;
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Public API
206
+ // ---------------------------------------------------------------------------
207
+
208
+ var KeymapRegistry = {
209
+
210
+ /**
211
+ * Register an action with its default key binding.
212
+ */
213
+ register: function (actionId, defaultBinding, description, group) {
214
+ actions.set(actionId, {
215
+ defaultBinding: defaultBinding,
216
+ description: description || '',
217
+ group: group || 'General',
218
+ });
219
+ },
220
+
221
+ /**
222
+ * Given a KeyboardEvent, return the matching actionId or null.
223
+ */
224
+ resolve: function (event) {
225
+ var matched = null;
226
+ actions.forEach(function (entry, actionId) {
227
+ if (matched) return; // already found
228
+ var binding = overrides.has(actionId)
229
+ ? overrides.get(actionId)
230
+ : entry.defaultBinding;
231
+ if (bindingMatchesEvent(binding, event)) {
232
+ matched = actionId;
233
+ }
234
+ });
235
+ return matched;
236
+ },
237
+
238
+ /**
239
+ * Return all registered actions grouped by their group name.
240
+ * Each entry contains actionId, description, binding, defaultBinding,
241
+ * and whether the binding has been overridden.
242
+ */
243
+ getAll: function () {
244
+ var groups = {};
245
+ actions.forEach(function (entry, actionId) {
246
+ var group = entry.group;
247
+ if (!groups[group]) groups[group] = [];
248
+ var currentBinding = overrides.has(actionId)
249
+ ? overrides.get(actionId)
250
+ : entry.defaultBinding;
251
+ groups[group].push({
252
+ actionId: actionId,
253
+ description: entry.description,
254
+ group: group,
255
+ binding: currentBinding,
256
+ defaultBinding: entry.defaultBinding,
257
+ overridden: overrides.has(actionId),
258
+ });
259
+ });
260
+ return groups;
261
+ },
262
+
263
+ /**
264
+ * Return the current binding for an action.
265
+ */
266
+ getBinding: function (actionId) {
267
+ if (overrides.has(actionId)) return overrides.get(actionId);
268
+ var entry = actions.get(actionId);
269
+ return entry ? entry.defaultBinding : null;
270
+ },
271
+
272
+ /**
273
+ * Set a user override for an action.
274
+ */
275
+ rebind: function (actionId, newBinding) {
276
+ overrides.set(actionId, newBinding);
277
+ saveOverrides();
278
+ notifyChange(actionId);
279
+ },
280
+
281
+ /**
282
+ * Remove the user override for a single action.
283
+ */
284
+ reset: function (actionId) {
285
+ if (overrides.has(actionId)) {
286
+ overrides.delete(actionId);
287
+ saveOverrides();
288
+ notifyChange(actionId);
289
+ }
290
+ },
291
+
292
+ /**
293
+ * Remove all user overrides.
294
+ */
295
+ resetAll: function () {
296
+ var ids = Array.from(overrides.keys());
297
+ overrides.clear();
298
+ saveOverrides();
299
+ ids.forEach(function (id) { notifyChange(id); });
300
+ },
301
+
302
+ /**
303
+ * Format a binding for display.
304
+ * Returns a string like "Ctrl+Alt+J" or "Cmd+Alt+J" on Mac.
305
+ */
306
+ formatBinding: function (binding) {
307
+ if (!binding) return '';
308
+ var parts = [];
309
+
310
+ // ctrlOrMeta / ctrlKey
311
+ var hasCtrlOrMeta = (binding.ctrlOrMeta === true) ||
312
+ (binding.ctrlKey === 'ctrlOrMeta') ||
313
+ (binding.ctrlOrMeta === 'ctrlOrMeta');
314
+ if (hasCtrlOrMeta) {
315
+ parts.push(isMac ? 'Cmd' : 'Ctrl');
316
+ } else if (binding.ctrlKey === true) {
317
+ parts.push('Ctrl');
318
+ }
319
+
320
+ if (binding.altKey) parts.push('Alt');
321
+ if (binding.shiftKey) parts.push('Shift');
322
+
323
+ // Key label
324
+ var keyLabel = null;
325
+ if (binding.code) {
326
+ // Convert code to a readable label
327
+ if (binding.code.startsWith('Digit')) {
328
+ keyLabel = binding.code.replace('Digit', '');
329
+ } else if (binding.code.startsWith('Key')) {
330
+ keyLabel = binding.code.replace('Key', '');
331
+ } else {
332
+ keyLabel = binding.code;
333
+ }
334
+ } else if (binding.key) {
335
+ // Friendly names for special keys
336
+ var friendlyKeys = {
337
+ 'ArrowLeft': 'Left',
338
+ 'ArrowRight': 'Right',
339
+ 'ArrowUp': 'Up',
340
+ 'ArrowDown': 'Down',
341
+ 'Escape': 'Esc',
342
+ ' ': 'Space',
343
+ 'Enter': 'Enter',
344
+ 'Backspace': 'Backspace',
345
+ 'Delete': 'Delete',
346
+ 'Tab': 'Tab',
347
+ };
348
+ keyLabel = friendlyKeys[binding.key] || binding.key;
349
+ }
350
+
351
+ if (keyLabel) parts.push(keyLabel);
352
+ return parts.join('+');
353
+ },
354
+
355
+ /**
356
+ * Given a KeyboardEvent, build and return a binding object.
357
+ * Used by the settings UI key-capture recorder.
358
+ */
359
+ captureBinding: function (event) {
360
+ var binding = {};
361
+
362
+ // Determine modifier state
363
+ if (isMac ? event.metaKey : event.ctrlKey) {
364
+ binding.ctrlOrMeta = true;
365
+ }
366
+ if (binding.ctrlOrMeta && isMac && event.ctrlKey) {
367
+ // User also pressed physical Ctrl on Mac alongside Cmd
368
+ binding.ctrlKey = true;
369
+ }
370
+ if (!isMac && event.metaKey && !binding.ctrlOrMeta) {
371
+ // Meta on non-Mac without ctrlOrMeta
372
+ binding.ctrlOrMeta = true;
373
+ }
374
+
375
+ binding.altKey = event.altKey || false;
376
+ binding.shiftKey = event.shiftKey || false;
377
+
378
+ // Key / code
379
+ if (!isModifierKey(event)) {
380
+ // Prefer code for letter and digit keys
381
+ if (event.code && (event.code.startsWith('Key') || event.code.startsWith('Digit'))) {
382
+ binding.code = event.code;
383
+ } else {
384
+ binding.key = event.key;
385
+ }
386
+ }
387
+ // If it's a modifier key, leave key/code absent (modifier-only combo)
388
+
389
+ return binding;
390
+ },
391
+
392
+ /**
393
+ * Export user overrides as a JSON string.
394
+ */
395
+ exportConfig: function () {
396
+ var obj = {};
397
+ overrides.forEach(function (binding, id) {
398
+ obj[id] = binding;
399
+ });
400
+ return JSON.stringify(obj, null, 2);
401
+ },
402
+
403
+ /**
404
+ * Import user overrides from a JSON string.
405
+ * Replaces all current overrides.
406
+ */
407
+ importConfig: function (json) {
408
+ var parsed;
409
+ try {
410
+ parsed = JSON.parse(json);
411
+ } catch (_) {
412
+ throw new Error('Invalid JSON for keymap config');
413
+ }
414
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
415
+ throw new Error('Keymap config must be a JSON object');
416
+ }
417
+ overrides.clear();
418
+ Object.keys(parsed).forEach(function (id) {
419
+ overrides.set(id, parsed[id]);
420
+ });
421
+ saveOverrides();
422
+ // Notify for all imported actions
423
+ Object.keys(parsed).forEach(function (id) { notifyChange(id); });
424
+ },
425
+
426
+ /**
427
+ * Register a listener for binding changes.
428
+ * Callback receives the actionId that changed.
429
+ * Returns an unsubscribe function.
430
+ */
431
+ onChange: function (callback) {
432
+ listeners.add(callback);
433
+ return function () { listeners.delete(callback); };
434
+ },
435
+
436
+ /**
437
+ * Check if a KeyboardEvent matches a specific action.
438
+ */
439
+ matchesAction: function (event, actionId) {
440
+ var binding = overrides.has(actionId)
441
+ ? overrides.get(actionId)
442
+ : (actions.has(actionId) ? actions.get(actionId).defaultBinding : null);
443
+ if (!binding) return false;
444
+ return bindingMatchesEvent(binding, event);
445
+ },
446
+
447
+ /** Expose platform detection for consumers */
448
+ isMac: isMac,
449
+ };
450
+
451
+ // ---------------------------------------------------------------------------
452
+ // Initialisation — load overrides, register defaults
453
+ // ---------------------------------------------------------------------------
454
+
455
+ loadOverrides();
456
+
457
+ // Shell group — All arrows navigate grid spatially
458
+ KeymapRegistry.register('shell:terminal-down',
459
+ { ctrlOrMeta: true, altKey: true, key: 'ArrowDown' },
460
+ 'Down terminal', 'Shell');
461
+
462
+ KeymapRegistry.register('shell:terminal-up',
463
+ { ctrlOrMeta: true, altKey: true, key: 'ArrowUp' },
464
+ 'Up terminal', 'Shell');
465
+
466
+ KeymapRegistry.register('shell:terminal-right',
467
+ { ctrlOrMeta: true, altKey: true, key: 'ArrowRight' },
468
+ 'Right terminal', 'Shell');
469
+
470
+ KeymapRegistry.register('shell:terminal-left',
471
+ { ctrlOrMeta: true, altKey: true, key: 'ArrowLeft' },
472
+ 'Left terminal', 'Shell');
473
+
474
+ for (var i = 1; i <= 9; i++) {
475
+ KeymapRegistry.register('shell:terminal-' + i,
476
+ { ctrlOrMeta: true, altKey: true, code: 'Digit' + i },
477
+ 'Terminal ' + i, 'Shell');
478
+ }
479
+
480
+ KeymapRegistry.register('shell:new-terminal',
481
+ { ctrlOrMeta: true, altKey: true, code: 'KeyJ' },
482
+ 'New terminal', 'Shell');
483
+
484
+ KeymapRegistry.register('shell:new-browser',
485
+ { ctrlOrMeta: true, altKey: true, code: 'KeyB' },
486
+ 'New browser', 'Shell');
487
+
488
+ KeymapRegistry.register('shell:close-pane',
489
+ { ctrlOrMeta: true, altKey: true, code: 'KeyK' },
490
+ 'Close pane', 'Shell');
491
+
492
+ // Voice group
493
+ KeymapRegistry.register('voice:hold-to-speak',
494
+ { ctrlOrMeta: true, altKey: true, shiftKey: true },
495
+ 'Hold to speak', 'Voice');
496
+
497
+ // Kanban group
498
+ KeymapRegistry.register('kanban:focus-search',
499
+ { key: '/' },
500
+ 'Focus search', 'Kanban');
501
+
502
+ KeymapRegistry.register('kanban:clear-search',
503
+ { key: 'Escape' },
504
+ 'Clear search', 'Kanban');
505
+
506
+ // ---------------------------------------------------------------------------
507
+ // Expose globally
508
+ // ---------------------------------------------------------------------------
509
+
510
+ window.KeymapRegistry = KeymapRegistry;
511
+
512
+ })();
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 32">
2
+ <rect width="32" height="32" rx="6" fill="#7c3aed"/>
3
+ <path d="M8 10h6l4 6-4 6H8l4-6-4-6z" fill="#fff"/>
4
+ <path d="M16 10h6l4 6-4 6h-6l4-6-4-6z" fill="#fff" opacity="0.6"/>
5
+ <text x="40" y="22" font-family="SF Mono, Cascadia Code, Fira Code, monospace" font-size="16" font-weight="600" fill="#e0e0e0">devglide</text>
6
+ </svg>
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@devglide/shared-assets",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "description": "Shared frontend assets (voice widget, voice listener, keymap registry) for DevGlide apps",
6
+ "main": "voice-widget.js",
7
+ "files": [
8
+ "*.js",
9
+ "*.d.ts"
10
+ ]
11
+ }
@@ -0,0 +1,48 @@
1
+ // ── Shared UI Utilities ──────────────────────────────────────────────────────
2
+
3
+ /**
4
+ * Escape HTML special characters to prevent XSS.
5
+ */
6
+ export function escapeHtml(str) {
7
+ return String(str).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
8
+ }
9
+
10
+ /**
11
+ * Escape string for use in HTML attributes.
12
+ */
13
+ export function escapeAttr(str) {
14
+ return (str || '').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
15
+ }
16
+
17
+ /**
18
+ * Convert literal \n and \t escape sequences to real characters.
19
+ */
20
+ export function normalizeEscapes(text) {
21
+ if (!text) return '';
22
+ return text.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
23
+ }
24
+
25
+ /**
26
+ * Format a date string as relative time (e.g., "5m ago", "2h ago").
27
+ */
28
+ export function timeAgo(dateStr) {
29
+ if (!dateStr) return 'never';
30
+ const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
31
+ if (seconds < 5) return 'just now';
32
+ if (seconds < 60) return seconds + 's ago';
33
+ const minutes = Math.floor(seconds / 60);
34
+ if (minutes < 60) return minutes + 'm ago';
35
+ const hours = Math.floor(minutes / 60);
36
+ if (hours < 24) return hours + 'h ago';
37
+ const days = Math.floor(hours / 24);
38
+ return days + 'd ago';
39
+ }
40
+
41
+ /**
42
+ * Format a duration in milliseconds to human-readable string.
43
+ */
44
+ export function formatDuration(ms) {
45
+ if (ms == null) return '';
46
+ if (ms < 1000) return ms + 'ms';
47
+ return (ms / 1000).toFixed(1) + 's';
48
+ }
@@ -0,0 +1,37 @@
1
+ export interface VoiceWidgetOptions {
2
+ /** Base URL of the voice app, e.g. 'http://localhost:7004' */
3
+ voiceUrl: string;
4
+ /** Called with the transcribed text when transcription succeeds */
5
+ onResult: (text: string) => void;
6
+ /** Called with an Error when something goes wrong (default: console.error) */
7
+ onError?: (err: Error) => void;
8
+ /** BCP-47 language tag, e.g. 'en'. Defaults to the voice app's configured language. */
9
+ language?: string;
10
+ /** Single-character keyboard shortcut to hold for recording, e.g. 'v' */
11
+ hotkey?: string;
12
+ /** Idle state label override (default: 'Speak') */
13
+ label?: string;
14
+ }
15
+
16
+ export interface VoiceWidgetInstance {
17
+ /** Render the mic button into the given container element */
18
+ mount(containerEl: HTMLElement): void;
19
+ /** Remove the widget and clean up all event listeners */
20
+ destroy(): void;
21
+ /** Programmatically start recording */
22
+ startRecording(): void;
23
+ /** Programmatically stop recording and trigger transcription */
24
+ stopRecording(): void;
25
+ /** Current widget state */
26
+ readonly state: 'idle' | 'recording' | 'transcribing' | 'error';
27
+ }
28
+
29
+ export interface VoiceWidgetFactory {
30
+ create(opts: VoiceWidgetOptions): VoiceWidgetInstance;
31
+ }
32
+
33
+ declare const VoiceWidget: VoiceWidgetFactory;
34
+ export default VoiceWidget;
35
+
36
+ // CommonJS / UMD
37
+ export { VoiceWidget };