@polderlabs/openkan 0.4.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 (114) hide show
  1. package/CHANGELOG.md +226 -0
  2. package/LICENSE +21 -0
  3. package/README.md +318 -0
  4. package/agents/openkan.md +254 -0
  5. package/bin/install-agent.mjs +63 -0
  6. package/bin/ok.mjs +17 -0
  7. package/bin/openkan.mjs +10 -0
  8. package/dist/.claude/skills/ok-planning/SKILL.md +285 -0
  9. package/dist/.claude/skills/ok-planning/references/integration.md +153 -0
  10. package/dist/.claude/skills/ok-planning/references/schemas.md +270 -0
  11. package/dist/.claude/skills/ok-planning/references/workflows.md +185 -0
  12. package/dist/.claude/skills/ok-planning/scripts/ok-init.sh +14 -0
  13. package/dist/.claude/skills/ok-planning/scripts/ok-resume.sh +38 -0
  14. package/dist/.claude/skills/ok-planning/scripts/ok-status.sh +24 -0
  15. package/dist/agents/openkan.md +254 -0
  16. package/dist/bin/install-agent.mjs +76 -0
  17. package/dist/bin/ok-install.js +58 -0
  18. package/dist/bin/ok.js +138 -0
  19. package/dist/bin/openkan.js +804 -0
  20. package/dist/commands/organize.md +15 -0
  21. package/dist/kanban/agent-profile.js +8 -0
  22. package/dist/kanban/archive.js +49 -0
  23. package/dist/kanban/bizar.js +242 -0
  24. package/dist/kanban/board.js +367 -0
  25. package/dist/kanban/bulk.js +139 -0
  26. package/dist/kanban/changelog.js +186 -0
  27. package/dist/kanban/chat.js +1280 -0
  28. package/dist/kanban/claude-state.js +974 -0
  29. package/dist/kanban/comments.js +80 -0
  30. package/dist/kanban/docs.js +144 -0
  31. package/dist/kanban/fs.js +163 -0
  32. package/dist/kanban/git.js +196 -0
  33. package/dist/kanban/images.js +140 -0
  34. package/dist/kanban/import.js +295 -0
  35. package/dist/kanban/inputs.js +94 -0
  36. package/dist/kanban/insights.js +140 -0
  37. package/dist/kanban/io.js +75 -0
  38. package/dist/kanban/mdx-render.js +348 -0
  39. package/dist/kanban/mdx.js +231 -0
  40. package/dist/kanban/projects.js +545 -0
  41. package/dist/kanban/search.js +121 -0
  42. package/dist/kanban/server.js +3296 -0
  43. package/dist/kanban/tags.js +124 -0
  44. package/dist/kanban/template.js +145 -0
  45. package/dist/kanban/tsx-sandbox.js +187 -0
  46. package/dist/kanban/watcher.js +270 -0
  47. package/dist/ok/commands/goal.js +65 -0
  48. package/dist/ok/commands/index.js +87 -0
  49. package/dist/ok/commands/init.js +15 -0
  50. package/dist/ok/commands/plan.js +155 -0
  51. package/dist/ok/commands/prd.js +202 -0
  52. package/dist/ok/commands/progress.js +31 -0
  53. package/dist/ok/commands/task.js +377 -0
  54. package/dist/ok/ids.js +98 -0
  55. package/dist/ok/lock.js +156 -0
  56. package/dist/ok/migrate.js +197 -0
  57. package/dist/ok/schemas.js +402 -0
  58. package/dist/ok/storage.js +222 -0
  59. package/dist/skills/openkan/SKILL.md +111 -0
  60. package/dist/skills/openkan/agents/openai.yaml +4 -0
  61. package/dist/skills/openkan/examples/simple-task.mdx +34 -0
  62. package/dist/skills/openkan/examples/with-ask.mdx +32 -0
  63. package/dist/skills/openkan/examples/with-choice.mdx +51 -0
  64. package/dist/skills/openkan/examples/with-preview.mdx +54 -0
  65. package/dist/skills/openkan/references/api.md +169 -0
  66. package/dist/skills/openkan/templates/task.mdx +46 -0
  67. package/dist/web/api.js +257 -0
  68. package/dist/web/app.js +4251 -0
  69. package/dist/web/bizar.js +39 -0
  70. package/dist/web/brand/agent-activity-sprite.svg +1 -0
  71. package/dist/web/brand/banner-docs.svg +24 -0
  72. package/dist/web/brand/banner.svg +32 -0
  73. package/dist/web/brand/empty-sessions.svg +17 -0
  74. package/dist/web/brand/empty-tasks.svg +17 -0
  75. package/dist/web/brand/favicon.svg +9 -0
  76. package/dist/web/brand/infinity-loader-animated.svg +220 -0
  77. package/dist/web/brand/infinity-loader-spritesheet.svg +230 -0
  78. package/dist/web/brand/logo-wordmark.svg +10 -0
  79. package/dist/web/brand/logo.svg +9 -0
  80. package/dist/web/brand/pixel-infinity-track.svg +1 -0
  81. package/dist/web/brand/social-card.svg +26 -0
  82. package/dist/web/changelog-view.js +456 -0
  83. package/dist/web/charts.js +269 -0
  84. package/dist/web/chat-sidebar.js +2397 -0
  85. package/dist/web/chat-status-motion.js +154 -0
  86. package/dist/web/claude-pane.js +820 -0
  87. package/dist/web/command-palette.js +381 -0
  88. package/dist/web/contributors-view.js +317 -0
  89. package/dist/web/cross-tab.js +102 -0
  90. package/dist/web/docs-view.js +168 -0
  91. package/dist/web/experience.css +165 -0
  92. package/dist/web/goals-view.js +45 -0
  93. package/dist/web/home-view.js +113 -0
  94. package/dist/web/images.js +311 -0
  95. package/dist/web/index.html +485 -0
  96. package/dist/web/insights.js +217 -0
  97. package/dist/web/keyboard.js +446 -0
  98. package/dist/web/mdx-viewer.js +600 -0
  99. package/dist/web/path-picker.js +787 -0
  100. package/dist/web/preview-frame.html +187 -0
  101. package/dist/web/settings.js +582 -0
  102. package/dist/web/style.css +8545 -0
  103. package/dist/web/task-view.js +1759 -0
  104. package/dist/web/vendor/gsap.min.js +11 -0
  105. package/dist/web/workspace.css +1513 -0
  106. package/package.json +71 -0
  107. package/skills/openkan/SKILL.md +111 -0
  108. package/skills/openkan/agents/openai.yaml +4 -0
  109. package/skills/openkan/examples/simple-task.mdx +34 -0
  110. package/skills/openkan/examples/with-ask.mdx +32 -0
  111. package/skills/openkan/examples/with-choice.mdx +51 -0
  112. package/skills/openkan/examples/with-preview.mdx +54 -0
  113. package/skills/openkan/references/api.md +169 -0
  114. package/skills/openkan/templates/task.mdx +46 -0
@@ -0,0 +1,485 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="color-scheme" content="dark light" />
7
+ <title>OpenKan</title>
8
+ <link rel="stylesheet" href="style.css" />
9
+ <link rel="stylesheet" href="workspace.css?v=20260904-board-width" />
10
+ <link rel="stylesheet" href="experience.css" />
11
+ <!-- M-Brand: SVG favicon, mono-coral. No PNG fallback (spec: no rasters). -->
12
+ <link rel="icon" type="image/svg+xml" href="./brand/favicon.svg" />
13
+ </head>
14
+ <body>
15
+ <!-- M13: skip-link for keyboard / screen-reader users. Hidden until
16
+ focused, then promoted to the top-left of the viewport. -->
17
+ <a href="#board" class="skip-link">Skip to main content</a>
18
+
19
+ <header class="topbar" role="banner">
20
+ <div class="brand">
21
+ <!-- M-Brand: wordmark image replaces the inline K-mark span and the
22
+ OpenKan <h1>. The image uses currentColor for ink-80 in the
23
+ topbar context. -->
24
+ <span class="brand-wordmark" role="img" aria-label="OpenKan"></span>
25
+ <h1 class="visually-hidden">OpenKan</h1>
26
+ <!-- Brand-level project chip: clicking it opens the same project
27
+ switcher dropdown as the topbar-right chip. Owned by app.js. -->
28
+ <button id="brand-project-chip" class="brand-project-chip" type="button"
29
+ title="Switch project" aria-label="Switch project">
30
+ <span class="brand-project-label">Project</span>
31
+ <span class="brand-project-value">
32
+ <span class="brand-project-dot" aria-hidden="true"></span>
33
+ <span id="brand-project-name" class="brand-project-name">Loading…</span>
34
+ </span>
35
+ <span class="brand-project-caret" aria-hidden="true">▾</span>
36
+ </button>
37
+ </div>
38
+ <button id="home-page-btn" class="topbar-home" type="button" aria-label="Open workspace overview">
39
+ <span aria-hidden="true">⌂</span><span>Overview</span>
40
+ </button>
41
+ <div class="workspace-mode" role="group" aria-label="Workspace mode">
42
+ <button type="button" data-workspace-mode="tasks" class="active">Tasks</button>
43
+ <button type="button" data-workspace-mode="chat">Chat</button>
44
+ </div>
45
+ <nav class="topbar-tabs" role="tablist" aria-label="Dashboard sections">
46
+ <button type="button" class="tab" role="tab" data-tab="tasks" aria-selected="true">Tasks</button>
47
+ <button type="button" class="tab" role="tab" data-tab="docs" aria-selected="false">Docs</button>
48
+ <button type="button" class="tab" role="tab" data-tab="goals" aria-selected="false">Goals</button>
49
+ <button type="button" class="tab" role="tab" data-tab="agents" aria-selected="false">Agents</button>
50
+ </nav>
51
+ <div class="topbar-right">
52
+ <span id="status-pill" class="pill pill-disconnected" role="status" aria-live="polite">
53
+ <span class="pill-dot" aria-hidden="true"></span>
54
+ <span id="status-text">Disconnected</span>
55
+ </span>
56
+ <button id="new-task-btn" class="btn btn-primary topbar-create" type="button">
57
+ <span class="btn-symbol" aria-hidden="true">+</span>
58
+ <span class="btn-label">New task</span>
59
+ </button>
60
+ <!-- Project switcher: button shows the active project's name; clicking
61
+ opens a small popover listing every registered project. Owned by
62
+ web/app.js (OpenKanProjectSwitcher). -->
63
+ <div class="project-switcher" id="project-switcher">
64
+ <button id="project-switcher-btn" class="project-switcher-btn" type="button"
65
+ aria-haspopup="menu" aria-expanded="false"
66
+ title="Switch project">
67
+ <span class="project-switcher-dot" aria-hidden="true"></span>
68
+ <span id="project-switcher-label" class="project-switcher-label">…</span>
69
+ <span class="project-switcher-caret" aria-hidden="true">▾</span>
70
+ </button>
71
+ <div id="project-switcher-popover" class="project-switcher-popover" role="menu" hidden></div>
72
+ </div>
73
+ <div class="workspace-more">
74
+ <button id="workspace-more-btn" class="btn-icon" type="button" aria-label="More workspace pages" aria-haspopup="menu" aria-expanded="false" title="More workspace pages">•••</button>
75
+ <div id="workspace-more-menu" class="workspace-more-menu" role="menu" hidden>
76
+ <span class="workspace-more-label">Workspace mode</span>
77
+ <button type="button" role="menuitem" data-workspace-mode="tasks">Task mode</button>
78
+ <button type="button" role="menuitem" data-workspace-mode="chat">Chat mode</button>
79
+ <span class="workspace-more-separator"></span>
80
+ <button type="button" role="menuitem" data-workspace-page="changelog">Changelog</button>
81
+ <button type="button" role="menuitem" data-workspace-page="insights">Insights</button>
82
+ <button type="button" role="menuitem" data-workspace-page="contributors">Contributors</button>
83
+ </div>
84
+ </div>
85
+ <button id="settings-btn" class="btn-icon" type="button" aria-label="Settings" title="Settings">⚙</button>
86
+ <button id="chat-sidebar-toggle-btn" class="btn-icon" type="button"
87
+ aria-label="Toggle chat sidebar" aria-controls="chat-sidebar"
88
+ aria-expanded="false" title="Chat sidebar (Alt+C)">💬</button>
89
+ <button id="palette-btn" class="btn-icon" type="button"
90
+ aria-label="Open command palette (Cmd/Ctrl K)"
91
+ title="Command palette (Cmd/Ctrl K)">⌘K</button>
92
+ </div>
93
+ </header>
94
+
95
+ <!-- Toast container (used by settings / changelog / contributors) -->
96
+ <div id="toast-container" class="toast-container" aria-live="polite" aria-atomic="true"></div>
97
+
98
+ <main class="dashboard">
99
+ <section id="tab-home" class="tab-pane" role="tabpanel" data-tab="home" hidden>
100
+ <div id="home-root" class="view-root"></div>
101
+ </section>
102
+ <!-- ─── TAB: Tasks ───────────────────────────────────────────────── -->
103
+ <section id="tab-tasks" class="tab-pane" role="tabpanel" data-tab="tasks">
104
+ <div class="workspace-intro">
105
+ <div>
106
+ <span class="workspace-eyebrow">Project workspace</span>
107
+ <h2>Move work forward</h2>
108
+ <p>Plan, coordinate, and review every task in one durable board.</p>
109
+ </div>
110
+ <div id="board-overview" class="board-overview" aria-label="Board summary">
111
+ <div class="overview-stat">
112
+ <span id="overview-total" class="overview-value">0</span>
113
+ <span class="overview-label">Visible</span>
114
+ </div>
115
+ <div class="overview-stat is-doing">
116
+ <span id="overview-doing" class="overview-value">0</span>
117
+ <span class="overview-label">In progress</span>
118
+ </div>
119
+ <div class="overview-stat is-review">
120
+ <span id="overview-review" class="overview-value">0</span>
121
+ <span class="overview-label">In review</span>
122
+ </div>
123
+ <div class="overview-stat is-done">
124
+ <span id="overview-done" class="overview-value">0</span>
125
+ <span class="overview-label">Done</span>
126
+ </div>
127
+ </div>
128
+ </div>
129
+
130
+ <!-- Saved filter chips row (populated by app.js). Hidden when empty. -->
131
+ <div id="saved-filters-bar" class="saved-filters-bar" hidden>
132
+ <span class="filter-bar-label">Saved</span>
133
+ <div id="saved-filters-list" class="saved-filters-list"></div>
134
+ </div>
135
+
136
+ <!-- Category / tag / sort filter bar. Populated + wired up by app.js. -->
137
+ <div class="filter-bar" id="filter-bar" role="region" aria-label="Filter tasks">
138
+ <div class="filter-primary">
139
+ <div class="filter-bar-row search" id="filter-search">
140
+ <span class="search-icon" aria-hidden="true"></span>
141
+ <input type="search" id="search-input" autocomplete="off" spellcheck="false"
142
+ placeholder="Search titles, descriptions, tags, MDX content…"
143
+ aria-label="Search tasks" />
144
+ <span class="search-meta" id="search-meta" aria-live="polite"></span>
145
+ </div>
146
+ <div class="filter-bar-row controls" id="filter-controls">
147
+ <label class="select-wrap" id="sort-select-wrap">
148
+ <span class="sr-only">Sort tasks</span>
149
+ <select id="sort-select" aria-label="Sort tasks">
150
+ <option value="newest">Newest first</option>
151
+ <option value="oldest">Oldest first</option>
152
+ <option value="priority">Priority (urgent→low)</option>
153
+ <option value="effort">Effort (xl→xs)</option>
154
+ <option value="activity">Last activity</option>
155
+ </select>
156
+ </label>
157
+ <button type="button" id="filter-toggle-btn" class="filter-toggle-btn"
158
+ aria-expanded="false" aria-controls="filter-advanced">
159
+ Filters <span id="filter-count" class="filter-count" hidden>0</span>
160
+ </button>
161
+ <div class="archive-toggle" role="group" aria-label="Archive filter">
162
+ <button type="button" data-archive="active" class="active">Active</button>
163
+ <button type="button" data-archive="archived">Archived</button>
164
+ <button type="button" data-archive="both">Both</button>
165
+ </div>
166
+ </div>
167
+ </div>
168
+
169
+ <div id="filter-advanced" class="filter-advanced" hidden>
170
+ <div class="filter-bar-row categories" id="filter-categories">
171
+ <span class="filter-bar-label">Category</span>
172
+ <button type="button" data-category="all" class="all active">All</button>
173
+ <button type="button" data-category="frontend" class="c-frontend">frontend</button>
174
+ <button type="button" data-category="backend" class="c-backend">backend</button>
175
+ <button type="button" data-category="infra" class="c-infra">infra</button>
176
+ <button type="button" data-category="docs" class="c-docs">docs</button>
177
+ <button type="button" data-category="test" class="c-test">test</button>
178
+ <button type="button" data-category="design" class="c-design">design</button>
179
+ <button type="button" data-category="data" class="c-data">data</button>
180
+ <button type="button" data-category="security" class="c-security">security</button>
181
+ <button type="button" data-category="task" class="c-task">task</button>
182
+ </div>
183
+ <div class="filter-bar-row tags" id="filter-tags">
184
+ <span class="filter-bar-label">Tags</span>
185
+ <button type="button" data-tag="bug" class="t-bug">bug</button>
186
+ <button type="button" data-tag="feature" class="t-feature">feature</button>
187
+ <button type="button" data-tag="refactor" class="t-refactor">refactor</button>
188
+ <button type="button" data-tag="docs" class="t-docs">docs</button>
189
+ <button type="button" data-tag="test" class="t-test">test</button>
190
+ <button type="button" data-tag="perf" class="t-perf">perf</button>
191
+ <button type="button" data-tag="security" class="t-security">security</button>
192
+ <button type="button" data-tag="a11y" class="t-a11y">a11y</button>
193
+ </div>
194
+ <div class="filter-bar-row contributors" id="filter-contributors">
195
+ <span class="filter-bar-label">Contributors</span>
196
+ <button type="button" data-contributor="all" class="active">all</button>
197
+ <button type="button" data-contributor="@me" class="contributor-me">@me</button>
198
+ <!-- Other contributors are populated by app.js from /api/contributors -->
199
+ </div>
200
+ <div class="filter-advanced-actions">
201
+ <button type="button" id="save-filter-btn" class="filter-bar-save" title="Save current filter">Save view</button>
202
+ <button type="button" class="filter-bar-clear" id="filter-clear" aria-label="Clear filters">Clear filters</button>
203
+ </div>
204
+ </div>
205
+ </div>
206
+
207
+ <div id="main-content">
208
+ <main id="board" class="board" role="region" aria-label="Kanban board" tabindex="-1">
209
+ <!-- Columns are rendered by app.js -->
210
+ </main>
211
+ </div>
212
+
213
+ <!-- New Task Modal — Title + Project path (with Browse) + Description
214
+ + Column / Agent / Model. Opened from the + New Task button in
215
+ the topbar. Lives inside <main class="dashboard"> which is fine:
216
+ the New Task button isn't reachable while the task view is open
217
+ (the topbar is overlaid by the task view), so the modal-collapse
218
+ bug doesn't apply here. -->
219
+ <div id="modal-backdrop" class="modal-backdrop" hidden>
220
+ <div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
221
+ <header class="modal-header">
222
+ <h2 id="modal-title">New Task</h2>
223
+ <button class="btn-icon" type="button" data-close-modal aria-label="Close">&times;</button>
224
+ </header>
225
+ <form id="new-task-form" class="modal-body" novalidate>
226
+ <p id="task-create-project" class="task-create-context">Create a task in the current project.</p>
227
+ <label class="field">
228
+ <span>Task title <span class="field-required">Required</span></span>
229
+ <input name="title" type="text" required maxlength="200" autocomplete="off" placeholder="What needs to get done?" aria-describedby="task-create-error" />
230
+ </label>
231
+ <label class="field">
232
+ <span>Description <span class="field-optional">Optional</span></span>
233
+ <textarea name="description" rows="4" placeholder="Describe the outcome, context, and how to verify it. Markdown supported."></textarea>
234
+ </label>
235
+ <div class="field-row">
236
+ <label class="field">
237
+ <span>Status</span>
238
+ <select name="column">
239
+ <option value="backlog">Backlog</option>
240
+ <option value="todo" selected>To Do</option>
241
+ <option value="doing">In Progress</option>
242
+ <option value="review">Review</option>
243
+ <option value="done">Done</option>
244
+ </select>
245
+ </label>
246
+ <label class="field">
247
+ <span>Priority</span>
248
+ <select name="priority">
249
+ <option value="low">Low</option>
250
+ <option value="normal" selected>Normal</option>
251
+ <option value="high">High</option>
252
+ <option value="urgent">Urgent</option>
253
+ </select>
254
+ </label>
255
+ </div>
256
+ <details class="task-create-options">
257
+ <summary>Agent settings <span>Optional</span></summary>
258
+ <p>Assign an agent and model for later. Creating a task does not start an agent.</p>
259
+ <div class="field-row">
260
+ <label class="field"><span>Agent</span><input name="agent" type="text" placeholder="Use configured default" autocomplete="off" /></label>
261
+ <label class="field"><span>Model</span><input name="model" type="text" placeholder="Use configured default" autocomplete="off" /></label>
262
+ </div>
263
+ </details>
264
+ <input type="hidden" name="parentId" value="" />
265
+ <p id="task-create-error" role="alert" hidden></p>
266
+ <label class="task-create-open"><input name="openAfterCreate" type="checkbox" /> Open task after creating</label>
267
+ <footer class="modal-footer">
268
+ <span class="task-create-shortcut">Ctrl / ⌘ + Enter to create</span>
269
+ <button type="button" class="btn" id="task-discard-draft">Discard draft</button>
270
+ <button type="button" class="btn" data-close-modal>Close</button>
271
+ <button type="submit" class="btn btn-primary" id="task-create-submit">Create task</button>
272
+ </footer>
273
+ </form>
274
+ </div>
275
+ </div>
276
+
277
+ <!-- Per-task action menu (positioned dynamically) -->
278
+ <div id="action-menu" class="action-menu" hidden role="menu"></div>
279
+
280
+ <!-- Bulk action bar (shown when ≥ 1 card is selected via Ctrl/Cmd-click) -->
281
+ <div id="bulk-bar" class="bulk-bar" hidden role="toolbar" aria-label="Bulk actions for selected tasks">
282
+ <span class="bulk-bar-count" id="bulk-bar-count">0 selected</span>
283
+ <div class="bulk-bar-group">
284
+ <div class="bulk-bar-dropdown" id="bulk-move-dropdown">
285
+ <button type="button" class="bulk-bar-btn" id="bulk-move-btn" aria-haspopup="menu" aria-expanded="false">
286
+ Move <span class="bulk-bar-caret" aria-hidden="true">▾</span>
287
+ </button>
288
+ <div class="bulk-bar-menu" id="bulk-move-menu" role="menu" hidden>
289
+ <button type="button" data-column="backlog" role="menuitem">Backlog</button>
290
+ <button type="button" data-column="todo" role="menuitem">To Do</button>
291
+ <button type="button" data-column="doing" role="menuitem">In Progress</button>
292
+ <button type="button" data-column="review" role="menuitem">Review</button>
293
+ <button type="button" data-column="done" role="menuitem">Done</button>
294
+ </div>
295
+ </div>
296
+ <div class="bulk-bar-dropdown" id="bulk-priority-dropdown">
297
+ <button type="button" class="bulk-bar-btn" id="bulk-priority-btn" aria-haspopup="menu" aria-expanded="false">
298
+ Priority <span class="bulk-bar-caret" aria-hidden="true">▾</span>
299
+ </button>
300
+ <div class="bulk-bar-menu" id="bulk-priority-menu" role="menu" hidden>
301
+ <button type="button" data-priority="urgent" role="menuitem">🚨 Urgent</button>
302
+ <button type="button" data-priority="high" role="menuitem">⬆ High</button>
303
+ <button type="button" data-priority="normal" role="menuitem">➡ Normal</button>
304
+ <button type="button" data-priority="low" role="menuitem">⬇ Low</button>
305
+ </div>
306
+ </div>
307
+ <button type="button" class="bulk-bar-btn" id="bulk-archive-btn" title="Archive all selected">
308
+ Archive
309
+ </button>
310
+ <button type="button" class="bulk-bar-btn danger" id="bulk-delete-btn" title="Delete all selected">
311
+ Delete
312
+ </button>
313
+ </div>
314
+ <span class="bulk-bar-spacer"></span>
315
+ <button type="button" class="bulk-bar-btn ghost" id="bulk-clear-btn" title="Clear selection (Esc)">
316
+ Clear
317
+ </button>
318
+ </div>
319
+ </section>
320
+
321
+ <!-- ─── TAB: Changelog ──────────────────────────────────────────── -->
322
+ <section id="tab-changelog" class="tab-pane" role="tabpanel" data-tab="changelog" hidden>
323
+ <div id="changelog-root" class="view-root"></div>
324
+ </section>
325
+
326
+ <!-- ─── TAB: Contributors ───────────────────────────────────────── -->
327
+ <section id="tab-contributors" class="tab-pane" role="tabpanel" data-tab="contributors" hidden>
328
+ <div id="contributors-root" class="view-root"></div>
329
+ </section>
330
+
331
+ <!-- ─── TAB: Docs ───────────────────────────────────────────────── -->
332
+ <section id="tab-docs" class="tab-pane" role="tabpanel" data-tab="docs" hidden>
333
+ <div class="docs-tab" id="docs-root"></div>
334
+ </section>
335
+
336
+ <!-- ─── TAB: Agents ─────────────────────────────────────────────── -->
337
+ <section id="tab-goals" class="tab-pane" role="tabpanel" data-tab="goals" hidden>
338
+ <div id="goals-root"></div>
339
+ </section>
340
+
341
+ <section id="tab-agents" class="tab-pane" role="tabpanel" data-tab="agents" hidden>
342
+ <div id="claude-pane-root" class="claude-pane view-root"></div>
343
+ </section>
344
+
345
+ <!-- ─── TAB: Insights ───────────────────────────────────────────── -->
346
+ <section id="tab-insights" class="tab-pane" role="tabpanel" data-tab="insights" hidden>
347
+ <div id="insights-root" class="view-root"></div>
348
+ </section>
349
+
350
+ <!-- ─── Add Project + Settings modals are top-level (below) ─────────
351
+ They used to live inside <main class="dashboard">. When a task
352
+ view opens, the dashboard gets display:none and the modals
353
+ collapsed to 0×0. They're now direct children of <body> so
354
+ they stay reachable regardless of which view is on top. -->
355
+ </main>
356
+
357
+ <!-- ─── Add Project modal — top-level so it's reachable while the ────
358
+ task view is open (when .dashboard collapses to display:none).
359
+ Originally nested inside <main class="dashboard">. -->
360
+ <div id="project-backdrop" class="modal-backdrop" hidden>
361
+ <div class="modal" role="dialog" aria-modal="true" aria-labelledby="project-title">
362
+ <header class="modal-header">
363
+ <h2 id="project-title">Add project</h2>
364
+ <button class="btn-icon" type="button" data-close-project aria-label="Close">&times;</button>
365
+ </header>
366
+ <form id="project-form" class="modal-body">
367
+ <label class="field">
368
+ <span>Name</span>
369
+ <input name="name" type="text" required maxlength="80" autocomplete="off" />
370
+ </label>
371
+ <label class="field">
372
+ <span>Path</span>
373
+ <div class="field-row-with-button">
374
+ <input name="root" type="text" required autocomplete="off"
375
+ placeholder="/absolute/path/to/project" />
376
+ <button type="button" id="project-browse-btn" class="btn btn-sm"
377
+ aria-label="Browse for a project folder">📁 Browse…</button>
378
+ </div>
379
+ </label>
380
+ <footer class="modal-footer">
381
+ <button type="button" class="btn" data-close-project>Cancel</button>
382
+ <button type="submit" class="btn btn-primary">Add project</button>
383
+ </footer>
384
+ </form>
385
+ </div>
386
+ </div>
387
+
388
+ <!-- ─── Settings dialog — top-level for the same reason as the Add ──
389
+ Project modal above. Owned by web/settings.js. -->
390
+ <div id="settings-backdrop" class="modal-backdrop" hidden>
391
+ <div class="modal settings-dialog" role="dialog" aria-modal="true" aria-labelledby="settings-title">
392
+ <header class="modal-header">
393
+ <h2 id="settings-title">Settings</h2>
394
+ <button class="btn-icon" type="button" data-close-settings aria-label="Close">&times;</button>
395
+ </header>
396
+ <div class="settings-layout">
397
+ <nav id="settings-nav" class="settings-nav" aria-label="Settings sections"></nav>
398
+ <div class="settings-pane">
399
+ <div class="modal-body" id="settings-body">
400
+ <!-- Populated by settings.js on open. -->
401
+ </div>
402
+ <footer class="modal-footer" id="settings-footer">
403
+ <button type="button" class="btn" data-close-settings>Cancel</button>
404
+ <button type="button" id="settings-save" class="btn btn-primary">Save</button>
405
+ </footer>
406
+ </div>
407
+ </div>
408
+ </div>
409
+ </div>
410
+
411
+ <!-- ─── Template help modal (single instance, reused) ──────────────── -->
412
+ <div id="template-backdrop" class="modal-backdrop" hidden>
413
+ <div class="modal template-dialog" role="dialog" aria-modal="true" aria-labelledby="template-title">
414
+ <header class="modal-header">
415
+ <h2 id="template-title">Task Template</h2>
416
+ <button class="btn-icon" type="button" data-close-template aria-label="Close">&times;</button>
417
+ </header>
418
+ <div class="modal-body" id="template-body">
419
+ <!-- Populated by task-view.js on open. -->
420
+ </div>
421
+ <footer class="modal-footer">
422
+ <button type="button" class="btn" id="template-copy-btn">Copy to clipboard</button>
423
+ <button type="button" class="btn btn-primary" data-close-template>Done</button>
424
+ </footer>
425
+ </div>
426
+ </div>
427
+
428
+ <!-- Task detail view (hidden by default; lives outside the .dashboard
429
+ panes so it overlays everything when a card is clicked) -->
430
+ <div id="task-view" hidden></div>
431
+
432
+ <!-- Image lightbox overlay (filled by images.js when an image is opened). -->
433
+ <div id="image-lightbox" class="image-lightbox" role="dialog" aria-modal="true" aria-label="Image preview" hidden tabindex="-1">
434
+ <img class="image-lightbox-img" alt="" />
435
+ <button type="button" class="image-lightbox-close" aria-label="Close">×</button>
436
+ </div>
437
+
438
+ <footer class="footer">
439
+ <span>OpenKan v0.3.0 · local-first workspace</span>
440
+ </footer>
441
+
442
+ <!-- api.js MUST load first; it exposes window.OpenKanAPI used by the others. -->
443
+ <script src="api.js" defer></script>
444
+ <!-- cross-tab.js exposes OpenKanCrossTab for sibling-tab sync (M13). -->
445
+ <script src="cross-tab.js" defer></script>
446
+ <script src="images.js" defer></script>
447
+ <script src="mdx-viewer.js" defer></script>
448
+ <script src="task-view.js" defer></script>
449
+ <script src="settings.js" defer></script>
450
+ <script src="changelog-view.js" defer></script>
451
+ <script src="contributors-view.js" defer></script>
452
+ <script src="docs-view.js" defer></script>
453
+ <!-- claude-pane.js exposes window.OpenKanClaude. It must load BEFORE
454
+ app.js (which calls OpenKanClaude.mount(...) when the Claude tab
455
+ is activated) but AFTER api.js (so window.OpenKanAPI.api exists). -->
456
+ <script src="claude-pane.js" defer></script>
457
+ <!-- GSAP powers the concise, status-specific chat work indicators. -->
458
+ <script src="vendor/gsap.min.js" defer></script>
459
+ <script src="chat-status-motion.js" defer></script>
460
+ <!-- chat-sidebar.js exposes window.OpenKanChatSidebar. It depends on
461
+ claude-pane.js (for the activity footer) and api.js (for fetch +
462
+ storage). Loaded before app.js so the toggle button can wire up.
463
+ Loaded before keyboard.js so its capture-phase Cmd/Ctrl+K handler
464
+ registers first and can intercept the palette-open shortcut while
465
+ the sidebar is open. -->
466
+ <script src="chat-sidebar.js" defer></script>
467
+ <!-- M13: keyboard module loads before the palette so the palette can
468
+ subscribe to "palette.open" / "palette.close" / "palette.isOpen". -->
469
+ <script src="keyboard.js" defer></script>
470
+ <script src="command-palette.js" defer></script>
471
+ <!-- path-picker.js exposes window.OpenKanPathPicker. It registers a
472
+ command-palette action and a Cmd/Ctrl+Shift+O shortcut on boot, so it
473
+ must load after command-palette.js but before app.js (which wires the
474
+ Browse button in the Add Project modal). -->
475
+ <script src="path-picker.js" defer></script>
476
+ <!-- charts.js / insights.js expose window.OpenKanCharts and
477
+ window.OpenKanInsights. They load AFTER api.js (window.OpenKanAPI)
478
+ and BEFORE app.js (which calls them from the tab router). -->
479
+ <script src="charts.js" defer></script>
480
+ <script src="insights.js" defer></script>
481
+ <script src="goals-view.js" defer></script>
482
+ <script src="home-view.js" defer></script>
483
+ <script src="app.js" defer></script>
484
+ </body>
485
+ </html>