paseo-beads 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/client/board-view.tsx +290 -0
- package/client/board.ts +160 -0
- package/client/focus.ts +51 -0
- package/client/format.ts +227 -0
- package/client/markdown-view.tsx +156 -0
- package/client/markdown.ts +435 -0
- package/client/overview-view.tsx +462 -0
- package/client/panel.tsx +961 -0
- package/client/project.ts +603 -0
- package/client/rows.tsx +666 -0
- package/client/styles.ts +557 -0
- package/images/board.png +0 -0
- package/images/overview.png +0 -0
- package/images/plan.png +0 -0
- package/images/risks.png +0 -0
- package/index.client.tsx +68 -0
- package/index.server.ts +21 -0
- package/package.json +53 -0
- package/paseo-plugin.json +6 -0
- package/server/attachments.ts +166 -0
- package/server/bv.ts +213 -0
- package/server/cache.ts +51 -0
- package/server/command.ts +343 -0
- package/server/dashboard.ts +244 -0
- package/server/issue.ts +59 -0
- package/server/normalize.ts +687 -0
- package/server/search.ts +40 -0
- package/server/tracker.ts +122 -0
- package/server/workspace.ts +153 -0
- package/shared/beads.ts +325 -0
- package/shared/rpc.ts +129 -0
package/client/styles.ts
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import type { PluginTheme } from "@getpaseo/plugin";
|
|
2
|
+
import type { TextStyle, ViewStyle } from "react-native";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Visual direction: a quiet dependency console shaped as a workbench. A single
|
|
6
|
+
* vertical rail carries priority colour; status is icon plus text, and metadata
|
|
7
|
+
* reads as restrained wrapping facets rather than SaaS pills. On a wide panel the
|
|
8
|
+
* workbench is a fixed-height master/detail pair of independent scroll regions
|
|
9
|
+
* (master slightly wider in operational views, board-dominant in Board view); on
|
|
10
|
+
* a compact panel the detail is a drill-in screen. No cards, no shadows, no
|
|
11
|
+
* gradients, no coloured panels, no hardcoded colours.
|
|
12
|
+
*/
|
|
13
|
+
export interface PanelStyles {
|
|
14
|
+
readonly screen: ViewStyle;
|
|
15
|
+
readonly topBar: ViewStyle;
|
|
16
|
+
readonly toolbarRow: ViewStyle;
|
|
17
|
+
readonly headerText: ViewStyle;
|
|
18
|
+
readonly title: TextStyle;
|
|
19
|
+
readonly subtitle: TextStyle;
|
|
20
|
+
readonly action: ViewStyle;
|
|
21
|
+
readonly actionInline: ViewStyle;
|
|
22
|
+
readonly actionPressed: ViewStyle;
|
|
23
|
+
readonly actionText: TextStyle;
|
|
24
|
+
readonly banner: ViewStyle;
|
|
25
|
+
readonly workbench: ViewStyle;
|
|
26
|
+
readonly masterPane: ViewStyle;
|
|
27
|
+
readonly masterPaneWide: ViewStyle;
|
|
28
|
+
readonly masterPaneAlone: ViewStyle;
|
|
29
|
+
readonly detailPaneNarrow: ViewStyle;
|
|
30
|
+
readonly detailPane: ViewStyle;
|
|
31
|
+
readonly paneScroll: ViewStyle;
|
|
32
|
+
readonly paneContent: ViewStyle;
|
|
33
|
+
readonly detailContent: ViewStyle;
|
|
34
|
+
readonly noticeContent: ViewStyle;
|
|
35
|
+
readonly switcherRow: ViewStyle;
|
|
36
|
+
readonly switcherItem: ViewStyle;
|
|
37
|
+
readonly switcherItemSelected: ViewStyle;
|
|
38
|
+
readonly switcherLabel: TextStyle;
|
|
39
|
+
readonly switcherLabelSelected: TextStyle;
|
|
40
|
+
readonly switcherCount: TextStyle;
|
|
41
|
+
readonly backRow: ViewStyle;
|
|
42
|
+
readonly listGroup: ViewStyle;
|
|
43
|
+
readonly stateBlock: ViewStyle;
|
|
44
|
+
readonly sectionHeader: ViewStyle;
|
|
45
|
+
readonly sectionTitle: TextStyle;
|
|
46
|
+
readonly sectionMeta: TextStyle;
|
|
47
|
+
readonly railRow: ViewStyle;
|
|
48
|
+
readonly railRowSelected: ViewStyle;
|
|
49
|
+
readonly rail: ViewStyle;
|
|
50
|
+
readonly railBody: ViewStyle;
|
|
51
|
+
readonly rowTitle: TextStyle;
|
|
52
|
+
readonly rowMeta: TextStyle;
|
|
53
|
+
readonly rowNote: TextStyle;
|
|
54
|
+
readonly metaRow: ViewStyle;
|
|
55
|
+
readonly facetRow: ViewStyle;
|
|
56
|
+
readonly facet: ViewStyle;
|
|
57
|
+
readonly facetIdent: ViewStyle;
|
|
58
|
+
readonly facetText: TextStyle;
|
|
59
|
+
readonly facetIdentText: TextStyle;
|
|
60
|
+
readonly facetDot: ViewStyle;
|
|
61
|
+
readonly facetStrongText: TextStyle;
|
|
62
|
+
readonly facetLabel: TextStyle;
|
|
63
|
+
readonly tagText: TextStyle;
|
|
64
|
+
readonly divider: ViewStyle;
|
|
65
|
+
readonly body: TextStyle;
|
|
66
|
+
readonly muted: TextStyle;
|
|
67
|
+
readonly danger: TextStyle;
|
|
68
|
+
readonly searchRow: ViewStyle;
|
|
69
|
+
readonly input: TextStyle;
|
|
70
|
+
readonly trackBlock: ViewStyle;
|
|
71
|
+
readonly detailStack: ViewStyle;
|
|
72
|
+
readonly detailBlock: ViewStyle;
|
|
73
|
+
readonly detailLabel: TextStyle;
|
|
74
|
+
readonly detailText: TextStyle;
|
|
75
|
+
readonly monoMeta: TextStyle;
|
|
76
|
+
readonly detailTitle: TextStyle;
|
|
77
|
+
readonly detailHeadBlock: ViewStyle;
|
|
78
|
+
readonly detailSection: ViewStyle;
|
|
79
|
+
readonly detailSectionLabel: TextStyle;
|
|
80
|
+
readonly commentBlock: ViewStyle;
|
|
81
|
+
readonly commentByline: TextStyle;
|
|
82
|
+
readonly markdownStack: ViewStyle;
|
|
83
|
+
readonly markdownParagraph: TextStyle;
|
|
84
|
+
readonly markdownHeading1: TextStyle;
|
|
85
|
+
readonly markdownHeading2: TextStyle;
|
|
86
|
+
readonly markdownHeading3: TextStyle;
|
|
87
|
+
readonly markdownStrong: TextStyle;
|
|
88
|
+
readonly markdownEmphasis: TextStyle;
|
|
89
|
+
readonly markdownInlineCode: TextStyle;
|
|
90
|
+
readonly markdownLink: TextStyle;
|
|
91
|
+
readonly markdownLinkTarget: TextStyle;
|
|
92
|
+
readonly markdownListRow: ViewStyle;
|
|
93
|
+
readonly markdownListMarker: TextStyle;
|
|
94
|
+
readonly markdownListText: TextStyle;
|
|
95
|
+
readonly markdownTaskMark: ViewStyle;
|
|
96
|
+
readonly markdownQuote: ViewStyle;
|
|
97
|
+
readonly markdownQuoteText: TextStyle;
|
|
98
|
+
readonly markdownRule: ViewStyle;
|
|
99
|
+
readonly markdownCodeBlock: ViewStyle;
|
|
100
|
+
readonly markdownCodeLanguage: TextStyle;
|
|
101
|
+
readonly markdownCodeText: TextStyle;
|
|
102
|
+
readonly markdownTruncated: TextStyle;
|
|
103
|
+
readonly boardPane: ViewStyle;
|
|
104
|
+
readonly boardStack: ViewStyle;
|
|
105
|
+
readonly boardScroll: ViewStyle;
|
|
106
|
+
readonly boardHeader: ViewStyle;
|
|
107
|
+
readonly boardHeaderStacked: ViewStyle;
|
|
108
|
+
readonly boardControlRow: ViewStyle;
|
|
109
|
+
readonly boardColumns: ViewStyle;
|
|
110
|
+
readonly boardColumn: ViewStyle;
|
|
111
|
+
readonly boardColumnHead: ViewStyle;
|
|
112
|
+
readonly boardColumnHeader: ViewStyle;
|
|
113
|
+
readonly boardColumnHint: TextStyle;
|
|
114
|
+
readonly boardColumnTitle: TextStyle;
|
|
115
|
+
readonly boardColumnCount: TextStyle;
|
|
116
|
+
readonly boardColumnBody: ViewStyle;
|
|
117
|
+
readonly boardCardParent: TextStyle;
|
|
118
|
+
readonly filterScroll: ViewStyle;
|
|
119
|
+
readonly filterRow: ViewStyle;
|
|
120
|
+
readonly filterChip: ViewStyle;
|
|
121
|
+
readonly filterChipLabel: ViewStyle;
|
|
122
|
+
readonly filterChipSelected: ViewStyle;
|
|
123
|
+
readonly groupMeta: TextStyle;
|
|
124
|
+
readonly progressText: TextStyle;
|
|
125
|
+
readonly segmentItemGrow: ViewStyle;
|
|
126
|
+
readonly segmentCaption: TextStyle;
|
|
127
|
+
readonly segmentRow: ViewStyle;
|
|
128
|
+
readonly segmentItem: ViewStyle;
|
|
129
|
+
readonly segmentItemSelected: ViewStyle;
|
|
130
|
+
readonly segmentLabel: TextStyle;
|
|
131
|
+
readonly segmentLabelSelected: TextStyle;
|
|
132
|
+
readonly progressTrack: ViewStyle;
|
|
133
|
+
readonly progressTrackWide: ViewStyle;
|
|
134
|
+
readonly progressFill: ViewStyle;
|
|
135
|
+
readonly overviewColumns: ViewStyle;
|
|
136
|
+
readonly overviewColumn: ViewStyle;
|
|
137
|
+
readonly summaryBlock: ViewStyle;
|
|
138
|
+
readonly summaryHeadRow: ViewStyle;
|
|
139
|
+
readonly summaryHeadline: TextStyle;
|
|
140
|
+
readonly summaryPercent: TextStyle;
|
|
141
|
+
readonly summaryFigures: ViewStyle;
|
|
142
|
+
readonly summaryFigure: ViewStyle;
|
|
143
|
+
readonly summaryFigureValue: TextStyle;
|
|
144
|
+
readonly summaryFigureLabel: TextStyle;
|
|
145
|
+
readonly rootBlock: ViewStyle;
|
|
146
|
+
readonly packageIndent: ViewStyle;
|
|
147
|
+
readonly groupRow: ViewStyle;
|
|
148
|
+
readonly groupRowHead: ViewStyle;
|
|
149
|
+
readonly groupTitle: TextStyle;
|
|
150
|
+
readonly groupTitleStrong: TextStyle;
|
|
151
|
+
readonly labelFacet: TextStyle;
|
|
152
|
+
readonly labelRow: ViewStyle;
|
|
153
|
+
readonly facetAccentText: TextStyle;
|
|
154
|
+
readonly boardCard: ViewStyle;
|
|
155
|
+
readonly boardCardSelected: ViewStyle;
|
|
156
|
+
readonly boardCardRail: ViewStyle;
|
|
157
|
+
readonly boardCardBody: ViewStyle;
|
|
158
|
+
readonly boardCardTitle: TextStyle;
|
|
159
|
+
readonly statusChip: ViewStyle;
|
|
160
|
+
readonly statusChipText: TextStyle;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Widest comfortable measure for prose-heavy inspector content. */
|
|
164
|
+
const DETAIL_MAX_WIDTH = 760;
|
|
165
|
+
|
|
166
|
+
export function createPanelStyles(theme: PluginTheme, compact: boolean): PanelStyles {
|
|
167
|
+
const gutter = compact ? 14 : 20;
|
|
168
|
+
const rowGap = compact ? 10 : 12;
|
|
169
|
+
const hitHeight = compact ? 46 : 42;
|
|
170
|
+
return {
|
|
171
|
+
screen: { flex: 1, backgroundColor: theme.colors.surface0 },
|
|
172
|
+
topBar: {
|
|
173
|
+
paddingHorizontal: gutter,
|
|
174
|
+
paddingTop: compact ? 10 : 8,
|
|
175
|
+
paddingBottom: compact ? 8 : 8,
|
|
176
|
+
gap: 6,
|
|
177
|
+
borderBottomWidth: 1,
|
|
178
|
+
borderBottomColor: theme.colors.border,
|
|
179
|
+
},
|
|
180
|
+
/**
|
|
181
|
+
* The panel's single chrome row: navigation, search and refresh together.
|
|
182
|
+
* It wraps rather than scrolls, so a narrow panel grows a line instead of
|
|
183
|
+
* hiding a control.
|
|
184
|
+
*/
|
|
185
|
+
toolbarRow: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: 8 },
|
|
186
|
+
headerText: { flex: 1, gap: 3 },
|
|
187
|
+
title: { color: theme.colors.foreground, fontSize: compact ? 16 : 18, fontWeight: "600" },
|
|
188
|
+
subtitle: { color: theme.colors.foregroundMuted, fontSize: 11 },
|
|
189
|
+
action: {
|
|
190
|
+
minHeight: compact ? 38 : 32,
|
|
191
|
+
justifyContent: "center",
|
|
192
|
+
paddingHorizontal: 12,
|
|
193
|
+
paddingVertical: 7,
|
|
194
|
+
borderWidth: 1,
|
|
195
|
+
borderColor: theme.colors.border,
|
|
196
|
+
borderRadius: 4,
|
|
197
|
+
backgroundColor: theme.colors.surface1,
|
|
198
|
+
},
|
|
199
|
+
actionInline: { alignSelf: "flex-start" },
|
|
200
|
+
actionPressed: { backgroundColor: theme.colors.surface2 },
|
|
201
|
+
actionText: { color: theme.colors.foreground, fontSize: 11, fontWeight: "600" },
|
|
202
|
+
banner: { paddingHorizontal: gutter, paddingTop: 12, gap: 6, maxWidth: DETAIL_MAX_WIDTH },
|
|
203
|
+
workbench: { flex: 1, flexDirection: "row" },
|
|
204
|
+
/**
|
|
205
|
+
* Operational views put the working list first: 5:4 favours the master pane
|
|
206
|
+
* without starving the inspector.
|
|
207
|
+
*/
|
|
208
|
+
masterPane: {
|
|
209
|
+
flex: 5,
|
|
210
|
+
minWidth: 0,
|
|
211
|
+
borderRightWidth: 1,
|
|
212
|
+
borderRightColor: theme.colors.border,
|
|
213
|
+
},
|
|
214
|
+
/** Board view: the columns need the width, the inspector stays reachable. */
|
|
215
|
+
masterPaneWide: { flex: 7 },
|
|
216
|
+
/** With no inspector beside it, the divider would sit on the panel edge. */
|
|
217
|
+
masterPaneAlone: { borderRightWidth: 0 },
|
|
218
|
+
detailPaneNarrow: { flex: 3 },
|
|
219
|
+
detailPane: { flex: 4, minWidth: 0 },
|
|
220
|
+
paneScroll: { flex: 1 },
|
|
221
|
+
paneContent: { paddingHorizontal: gutter, paddingTop: rowGap, paddingBottom: gutter * 2, gap: rowGap },
|
|
222
|
+
detailContent: {
|
|
223
|
+
paddingHorizontal: gutter,
|
|
224
|
+
paddingTop: rowGap,
|
|
225
|
+
paddingBottom: gutter * 2,
|
|
226
|
+
gap: rowGap,
|
|
227
|
+
width: "100%",
|
|
228
|
+
maxWidth: DETAIL_MAX_WIDTH,
|
|
229
|
+
},
|
|
230
|
+
noticeContent: { paddingHorizontal: gutter, paddingTop: gutter, paddingBottom: gutter * 2, gap: 8, maxWidth: DETAIL_MAX_WIDTH },
|
|
231
|
+
switcherRow: { flexDirection: "row", flexWrap: "wrap", gap: 8 },
|
|
232
|
+
switcherItem: {
|
|
233
|
+
minHeight: compact ? 40 : 34,
|
|
234
|
+
justifyContent: "center",
|
|
235
|
+
flexDirection: "row",
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
gap: 6,
|
|
238
|
+
paddingHorizontal: 12,
|
|
239
|
+
paddingVertical: 7,
|
|
240
|
+
borderWidth: 1,
|
|
241
|
+
borderColor: theme.colors.border,
|
|
242
|
+
borderRadius: 4,
|
|
243
|
+
},
|
|
244
|
+
switcherItemSelected: { backgroundColor: theme.colors.surface2, borderColor: theme.colors.accent },
|
|
245
|
+
switcherLabel: { color: theme.colors.foregroundMuted, fontSize: 12 },
|
|
246
|
+
switcherLabelSelected: { color: theme.colors.foreground, fontWeight: "600" },
|
|
247
|
+
switcherCount: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
248
|
+
backRow: { flexDirection: "row", alignItems: "center", gap: 12 },
|
|
249
|
+
listGroup: { gap: compact ? 2 : 4 },
|
|
250
|
+
stateBlock: { gap: 8 },
|
|
251
|
+
sectionHeader: {
|
|
252
|
+
flexDirection: "row",
|
|
253
|
+
alignItems: "baseline",
|
|
254
|
+
justifyContent: "space-between",
|
|
255
|
+
gap: 10,
|
|
256
|
+
paddingTop: rowGap,
|
|
257
|
+
paddingBottom: 2,
|
|
258
|
+
},
|
|
259
|
+
sectionTitle: {
|
|
260
|
+
color: theme.colors.foreground,
|
|
261
|
+
fontSize: 12,
|
|
262
|
+
fontWeight: "700",
|
|
263
|
+
letterSpacing: 0.4,
|
|
264
|
+
},
|
|
265
|
+
sectionMeta: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
266
|
+
railRow: { flexDirection: "row", gap: 12, paddingVertical: 8, paddingRight: 4, minHeight: hitHeight },
|
|
267
|
+
railRowSelected: { backgroundColor: theme.colors.surface1 },
|
|
268
|
+
rail: { width: 3, borderRadius: 2, alignSelf: "stretch", minHeight: 20 },
|
|
269
|
+
railBody: { flex: 1, gap: 3 },
|
|
270
|
+
rowTitle: { color: theme.colors.foreground, fontSize: 13, lineHeight: 18, fontWeight: "600" },
|
|
271
|
+
rowMeta: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
272
|
+
rowNote: { color: theme.colors.foregroundMuted, fontSize: 11, lineHeight: 16 },
|
|
273
|
+
metaRow: { flexDirection: "row", flexWrap: "wrap", gap: 6 },
|
|
274
|
+
/** Structured facets replace dot-joined metadata; they wrap instead of truncating. */
|
|
275
|
+
facetRow: { flexDirection: "row", flexWrap: "wrap", columnGap: 10, rowGap: 4, alignItems: "center" },
|
|
276
|
+
facet: { flexDirection: "row", alignItems: "center", gap: 4 },
|
|
277
|
+
/** Only the identifier gets a container, so it anchors the row without noise. */
|
|
278
|
+
facetIdent: {
|
|
279
|
+
flexDirection: "row",
|
|
280
|
+
alignItems: "center",
|
|
281
|
+
gap: 4,
|
|
282
|
+
paddingHorizontal: 5,
|
|
283
|
+
paddingVertical: 1,
|
|
284
|
+
borderWidth: 1,
|
|
285
|
+
borderColor: theme.colors.border,
|
|
286
|
+
borderRadius: 3,
|
|
287
|
+
backgroundColor: theme.colors.surface1,
|
|
288
|
+
},
|
|
289
|
+
facetText: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
290
|
+
facetIdentText: { color: theme.colors.foreground, fontSize: 10, fontWeight: "600" },
|
|
291
|
+
/** The priority swatch: the only colour a facet row carries. */
|
|
292
|
+
facetDot: { width: 6, height: 6, borderRadius: 3 },
|
|
293
|
+
facetStrongText: { color: theme.colors.foreground, fontSize: 10, fontWeight: "600" },
|
|
294
|
+
facetLabel: { color: theme.colors.foregroundMuted, fontSize: 9, letterSpacing: 0.3 },
|
|
295
|
+
tagText: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
296
|
+
divider: { height: 1, backgroundColor: theme.colors.border, opacity: 0.6 },
|
|
297
|
+
body: { color: theme.colors.foreground, fontSize: 12, lineHeight: 18 },
|
|
298
|
+
muted: { color: theme.colors.foregroundMuted, fontSize: 11, lineHeight: 16 },
|
|
299
|
+
danger: { color: theme.colors.statusDanger, fontSize: 11, lineHeight: 16 },
|
|
300
|
+
/** Grows into whatever the toolbar's chips and buttons leave behind. */
|
|
301
|
+
searchRow: {
|
|
302
|
+
flexGrow: 1,
|
|
303
|
+
flexShrink: 1,
|
|
304
|
+
minWidth: 160,
|
|
305
|
+
flexDirection: "row",
|
|
306
|
+
gap: 8,
|
|
307
|
+
alignItems: "center",
|
|
308
|
+
},
|
|
309
|
+
input: {
|
|
310
|
+
flex: 1,
|
|
311
|
+
minHeight: compact ? 40 : 34,
|
|
312
|
+
borderWidth: 1,
|
|
313
|
+
borderColor: theme.colors.border,
|
|
314
|
+
borderRadius: 4,
|
|
315
|
+
paddingHorizontal: 10,
|
|
316
|
+
paddingVertical: compact ? 8 : 7,
|
|
317
|
+
color: theme.colors.foreground,
|
|
318
|
+
backgroundColor: theme.colors.surface1,
|
|
319
|
+
fontSize: 12,
|
|
320
|
+
},
|
|
321
|
+
trackBlock: { gap: 2, paddingTop: 4 },
|
|
322
|
+
detailStack: { gap: 6 },
|
|
323
|
+
detailBlock: { gap: 4, paddingTop: 6 },
|
|
324
|
+
detailLabel: {
|
|
325
|
+
color: theme.colors.foregroundMuted,
|
|
326
|
+
fontSize: 10,
|
|
327
|
+
fontWeight: "600",
|
|
328
|
+
letterSpacing: 0.4,
|
|
329
|
+
},
|
|
330
|
+
detailText: { color: theme.colors.foreground, fontSize: 12, lineHeight: 18 },
|
|
331
|
+
monoMeta: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
332
|
+
detailTitle: {
|
|
333
|
+
color: theme.colors.foreground,
|
|
334
|
+
fontSize: compact ? 17 : 19,
|
|
335
|
+
lineHeight: compact ? 23 : 26,
|
|
336
|
+
fontWeight: "700",
|
|
337
|
+
},
|
|
338
|
+
detailHeadBlock: { gap: 8, paddingBottom: 4 },
|
|
339
|
+
detailSection: {
|
|
340
|
+
gap: 6,
|
|
341
|
+
paddingTop: compact ? 12 : 14,
|
|
342
|
+
borderTopWidth: 1,
|
|
343
|
+
borderTopColor: theme.colors.border,
|
|
344
|
+
},
|
|
345
|
+
detailSectionLabel: {
|
|
346
|
+
color: theme.colors.foregroundMuted,
|
|
347
|
+
fontSize: 10,
|
|
348
|
+
fontWeight: "700",
|
|
349
|
+
letterSpacing: 0.5,
|
|
350
|
+
},
|
|
351
|
+
commentBlock: { gap: 3, paddingTop: 8 },
|
|
352
|
+
commentByline: { color: theme.colors.foregroundMuted, fontSize: 10, fontWeight: "600" },
|
|
353
|
+
markdownStack: { gap: 6 },
|
|
354
|
+
markdownParagraph: { color: theme.colors.foreground, fontSize: 12, lineHeight: 19 },
|
|
355
|
+
markdownHeading1: {
|
|
356
|
+
color: theme.colors.foreground,
|
|
357
|
+
fontSize: 14,
|
|
358
|
+
lineHeight: 20,
|
|
359
|
+
fontWeight: "700",
|
|
360
|
+
paddingTop: 4,
|
|
361
|
+
},
|
|
362
|
+
markdownHeading2: {
|
|
363
|
+
color: theme.colors.foreground,
|
|
364
|
+
fontSize: 13,
|
|
365
|
+
lineHeight: 19,
|
|
366
|
+
fontWeight: "700",
|
|
367
|
+
paddingTop: 4,
|
|
368
|
+
},
|
|
369
|
+
markdownHeading3: {
|
|
370
|
+
color: theme.colors.foregroundMuted,
|
|
371
|
+
fontSize: 12,
|
|
372
|
+
lineHeight: 18,
|
|
373
|
+
fontWeight: "700",
|
|
374
|
+
letterSpacing: 0.3,
|
|
375
|
+
paddingTop: 2,
|
|
376
|
+
},
|
|
377
|
+
markdownStrong: { fontWeight: "700" },
|
|
378
|
+
markdownEmphasis: { fontStyle: "italic" },
|
|
379
|
+
markdownInlineCode: {
|
|
380
|
+
color: theme.colors.foreground,
|
|
381
|
+
backgroundColor: theme.colors.surface2,
|
|
382
|
+
fontFamily: "monospace",
|
|
383
|
+
fontSize: 11,
|
|
384
|
+
},
|
|
385
|
+
markdownLink: { color: theme.colors.accent, fontSize: 12, lineHeight: 19 },
|
|
386
|
+
markdownLinkTarget: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
387
|
+
markdownListRow: { flexDirection: "row", gap: 6, alignItems: "flex-start" },
|
|
388
|
+
markdownListMarker: {
|
|
389
|
+
color: theme.colors.foregroundMuted,
|
|
390
|
+
fontSize: 12,
|
|
391
|
+
lineHeight: 19,
|
|
392
|
+
minWidth: 16,
|
|
393
|
+
},
|
|
394
|
+
markdownListText: { flex: 1, color: theme.colors.foreground, fontSize: 12, lineHeight: 19 },
|
|
395
|
+
markdownTaskMark: { minWidth: 16, paddingTop: 3 },
|
|
396
|
+
markdownQuote: {
|
|
397
|
+
borderLeftWidth: 2,
|
|
398
|
+
borderLeftColor: theme.colors.border,
|
|
399
|
+
paddingLeft: 8,
|
|
400
|
+
paddingVertical: 2,
|
|
401
|
+
},
|
|
402
|
+
markdownQuoteText: {
|
|
403
|
+
color: theme.colors.foregroundMuted,
|
|
404
|
+
fontSize: 12,
|
|
405
|
+
lineHeight: 19,
|
|
406
|
+
fontStyle: "italic",
|
|
407
|
+
},
|
|
408
|
+
markdownRule: { height: 1, backgroundColor: theme.colors.border, marginVertical: 4 },
|
|
409
|
+
markdownCodeBlock: {
|
|
410
|
+
gap: 1,
|
|
411
|
+
paddingHorizontal: 10,
|
|
412
|
+
paddingVertical: 8,
|
|
413
|
+
borderWidth: 1,
|
|
414
|
+
borderColor: theme.colors.border,
|
|
415
|
+
borderRadius: 4,
|
|
416
|
+
backgroundColor: theme.colors.surface1,
|
|
417
|
+
},
|
|
418
|
+
markdownCodeLanguage: { color: theme.colors.foregroundMuted, fontSize: 9, letterSpacing: 0.4 },
|
|
419
|
+
markdownCodeText: {
|
|
420
|
+
color: theme.colors.foreground,
|
|
421
|
+
fontFamily: "monospace",
|
|
422
|
+
fontSize: 11,
|
|
423
|
+
lineHeight: 16,
|
|
424
|
+
},
|
|
425
|
+
markdownTruncated: { color: theme.colors.foregroundMuted, fontSize: 10, fontStyle: "italic" },
|
|
426
|
+
boardPane: { flex: 1, minWidth: 0 },
|
|
427
|
+
/** Compact board: laid out inside the page scroll, so no flex height. */
|
|
428
|
+
boardStack: { gap: 14 },
|
|
429
|
+
boardScroll: { flex: 1 },
|
|
430
|
+
/** Compact reuses the page gutter, so only the vertical rhythm is set here. */
|
|
431
|
+
boardHeaderStacked: { gap: 6, paddingBottom: 2 },
|
|
432
|
+
boardHeader: {
|
|
433
|
+
paddingHorizontal: gutter,
|
|
434
|
+
paddingTop: 8,
|
|
435
|
+
paddingBottom: 8,
|
|
436
|
+
gap: 6,
|
|
437
|
+
borderBottomWidth: 1,
|
|
438
|
+
borderBottomColor: theme.colors.border,
|
|
439
|
+
},
|
|
440
|
+
/** The board's own controls and its scope note share one line. */
|
|
441
|
+
boardControlRow: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: 10 },
|
|
442
|
+
/**
|
|
443
|
+
* Wide board: one flex column per state, each with its own vertical
|
|
444
|
+
* scroll, so a long column never pushes a short one out of view.
|
|
445
|
+
*/
|
|
446
|
+
boardColumns: { flex: 1, flexDirection: "row", gap: 12, paddingHorizontal: gutter, paddingTop: 10 },
|
|
447
|
+
boardColumn: { flex: 1, minWidth: 0, gap: 8 },
|
|
448
|
+
boardColumnHead: {
|
|
449
|
+
gap: 3,
|
|
450
|
+
paddingBottom: 6,
|
|
451
|
+
borderBottomWidth: 1,
|
|
452
|
+
borderBottomColor: theme.colors.border,
|
|
453
|
+
},
|
|
454
|
+
boardColumnHeader: { flexDirection: "row", alignItems: "center", gap: 6 },
|
|
455
|
+
/** The state's definition, so a column's name never has to be guessed. */
|
|
456
|
+
boardColumnHint: { color: theme.colors.foregroundMuted, fontSize: 10, lineHeight: 14 },
|
|
457
|
+
boardColumnTitle: { color: theme.colors.foreground, fontSize: 11, fontWeight: "700", letterSpacing: 0.4 },
|
|
458
|
+
boardColumnCount: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
459
|
+
boardColumnBody: { gap: 6, paddingBottom: gutter * 2 },
|
|
460
|
+
filterScroll: { flexGrow: 1, flexShrink: 1, minWidth: 0 },
|
|
461
|
+
filterRow: { flexDirection: "row", alignItems: "center", gap: 6 },
|
|
462
|
+
filterChip: {
|
|
463
|
+
flexDirection: "row",
|
|
464
|
+
alignItems: "center",
|
|
465
|
+
gap: 6,
|
|
466
|
+
maxWidth: 260,
|
|
467
|
+
minHeight: compact ? 34 : 26,
|
|
468
|
+
paddingHorizontal: 10,
|
|
469
|
+
paddingVertical: 4,
|
|
470
|
+
borderWidth: 1,
|
|
471
|
+
borderColor: theme.colors.border,
|
|
472
|
+
borderRadius: 4,
|
|
473
|
+
},
|
|
474
|
+
/** A project label reads as a label, so it keeps the label chip's fill. */
|
|
475
|
+
filterChipLabel: { backgroundColor: theme.colors.surface1 },
|
|
476
|
+
filterChipSelected: { backgroundColor: theme.colors.surface2, borderColor: theme.colors.accent },
|
|
477
|
+
/** Text beside a progress bar, and group metadata in the Overview. */
|
|
478
|
+
groupMeta: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
479
|
+
progressText: { color: theme.colors.foreground, fontSize: 11, fontWeight: "600", minWidth: 34, textAlign: "right" },
|
|
480
|
+
segmentCaption: { color: theme.colors.foregroundMuted, fontSize: 10, letterSpacing: 0.3 },
|
|
481
|
+
/** Board controls are subordinate to the view tabs, so they are smaller and borderless until chosen. */
|
|
482
|
+
segmentRow: {
|
|
483
|
+
flexDirection: "row",
|
|
484
|
+
borderWidth: 1,
|
|
485
|
+
borderColor: theme.colors.border,
|
|
486
|
+
borderRadius: 4,
|
|
487
|
+
overflow: "hidden",
|
|
488
|
+
},
|
|
489
|
+
segmentItem: {
|
|
490
|
+
minHeight: compact ? 34 : 26,
|
|
491
|
+
justifyContent: "center",
|
|
492
|
+
paddingHorizontal: 10,
|
|
493
|
+
paddingVertical: 4,
|
|
494
|
+
},
|
|
495
|
+
segmentItemSelected: { backgroundColor: theme.colors.surface2 },
|
|
496
|
+
/** Compact column picker: the states share the row evenly. */
|
|
497
|
+
segmentItemGrow: { flex: 1, alignItems: "center" },
|
|
498
|
+
segmentLabel: { color: theme.colors.foregroundMuted, fontSize: 11 },
|
|
499
|
+
segmentLabelSelected: { color: theme.colors.foreground, fontSize: 11, fontWeight: "600" },
|
|
500
|
+
progressTrack: {
|
|
501
|
+
width: 72,
|
|
502
|
+
height: 4,
|
|
503
|
+
borderRadius: 2,
|
|
504
|
+
backgroundColor: theme.colors.surface2,
|
|
505
|
+
overflow: "hidden",
|
|
506
|
+
},
|
|
507
|
+
progressTrackWide: { width: "100%" },
|
|
508
|
+
progressFill: { height: "100%", borderRadius: 2 },
|
|
509
|
+
/** Two columns on a wide pane, one when it is too narrow for both. */
|
|
510
|
+
overviewColumns: { flexDirection: "row", flexWrap: "wrap", gap: compact ? 12 : 28, alignItems: "flex-start" },
|
|
511
|
+
overviewColumn: { flexGrow: 1, flexShrink: 1, flexBasis: 340, minWidth: 0, gap: rowGap },
|
|
512
|
+
summaryBlock: { gap: 8, paddingTop: 4 },
|
|
513
|
+
summaryHeadRow: { flexDirection: "row", alignItems: "baseline", justifyContent: "space-between", gap: 10 },
|
|
514
|
+
summaryHeadline: { color: theme.colors.foreground, fontSize: compact ? 18 : 20, fontWeight: "700" },
|
|
515
|
+
summaryPercent: { color: theme.colors.foregroundMuted, fontSize: 13, fontWeight: "600" },
|
|
516
|
+
summaryFigures: { flexDirection: "row", flexWrap: "wrap", gap: compact ? 16 : 24, paddingTop: 2 },
|
|
517
|
+
summaryFigure: { gap: 1 },
|
|
518
|
+
summaryFigureValue: { fontSize: 18, fontWeight: "700" },
|
|
519
|
+
summaryFigureLabel: { color: theme.colors.foregroundMuted, fontSize: 10, letterSpacing: 0.3 },
|
|
520
|
+
rootBlock: { gap: 2, paddingBottom: 6 },
|
|
521
|
+
packageIndent: { paddingLeft: 14 },
|
|
522
|
+
groupRow: { gap: 4, paddingVertical: 6, paddingHorizontal: 4, borderRadius: 3 },
|
|
523
|
+
groupRowHead: { flexDirection: "row", alignItems: "baseline", gap: 10 },
|
|
524
|
+
groupTitle: { flex: 1, color: theme.colors.foreground, fontSize: 12 },
|
|
525
|
+
groupTitleStrong: { flex: 1, color: theme.colors.foreground, fontSize: 13, fontWeight: "700" },
|
|
526
|
+
labelRow: { flexDirection: "row", alignItems: "center", gap: 10, paddingVertical: 3 },
|
|
527
|
+
/** A project label, written as the project wrote it. */
|
|
528
|
+
labelFacet: {
|
|
529
|
+
color: theme.colors.foregroundMuted,
|
|
530
|
+
fontSize: 10,
|
|
531
|
+
paddingHorizontal: 4,
|
|
532
|
+
borderRadius: 3,
|
|
533
|
+
backgroundColor: theme.colors.surface2,
|
|
534
|
+
},
|
|
535
|
+
facetAccentText: { color: theme.colors.accent, fontSize: 10, fontWeight: "600" },
|
|
536
|
+
boardCard: {
|
|
537
|
+
flexDirection: "row",
|
|
538
|
+
gap: 10,
|
|
539
|
+
minHeight: hitHeight,
|
|
540
|
+
paddingVertical: 6,
|
|
541
|
+
paddingRight: 8,
|
|
542
|
+
paddingLeft: 0,
|
|
543
|
+
borderWidth: 1,
|
|
544
|
+
borderColor: theme.colors.border,
|
|
545
|
+
borderRadius: 4,
|
|
546
|
+
backgroundColor: theme.colors.surface1,
|
|
547
|
+
overflow: "hidden",
|
|
548
|
+
},
|
|
549
|
+
boardCardSelected: { backgroundColor: theme.colors.surface2, borderColor: theme.colors.accent },
|
|
550
|
+
boardCardRail: { width: 3, alignSelf: "stretch" },
|
|
551
|
+
boardCardBody: { flex: 1, gap: 4 },
|
|
552
|
+
boardCardParent: { color: theme.colors.foregroundMuted, fontSize: 10 },
|
|
553
|
+
boardCardTitle: { color: theme.colors.foreground, fontSize: 12, lineHeight: 17, fontWeight: "600" },
|
|
554
|
+
statusChip: { flexDirection: "row", alignItems: "center", gap: 4 },
|
|
555
|
+
statusChipText: { color: theme.colors.foreground, fontSize: 10, fontWeight: "600" },
|
|
556
|
+
};
|
|
557
|
+
}
|
package/images/board.png
ADDED
|
Binary file
|
|
Binary file
|
package/images/plan.png
ADDED
|
Binary file
|
package/images/risks.png
ADDED
|
Binary file
|
package/index.client.tsx
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { PluginClientContext } from "@getpaseo/plugin/client";
|
|
2
|
+
import { BeadsPanel } from "./client/panel";
|
|
3
|
+
import { requestDashboardRefresh, requestIssueFocus } from "./client/focus";
|
|
4
|
+
import { IssueIdSchema } from "./shared/beads";
|
|
5
|
+
import { beadsAttachmentSource } from "./shared/rpc";
|
|
6
|
+
|
|
7
|
+
const PANEL_ID = "beads";
|
|
8
|
+
|
|
9
|
+
export default function contribute(client: PluginClientContext) {
|
|
10
|
+
client.addWorkspacePanel({
|
|
11
|
+
id: PANEL_ID,
|
|
12
|
+
title: "Beads",
|
|
13
|
+
icon: "GitFork",
|
|
14
|
+
context: "workspace",
|
|
15
|
+
locations: ["workspace", "explorer"],
|
|
16
|
+
Component: BeadsPanel,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
client.addCommandCenterItem({
|
|
20
|
+
id: "beads-open",
|
|
21
|
+
title: "Open Beads",
|
|
22
|
+
icon: "GitFork",
|
|
23
|
+
keywords: ["beads", "issues", "dependencies", "triage", "ready"],
|
|
24
|
+
context: "workspace",
|
|
25
|
+
onSelect({ openPanel }) {
|
|
26
|
+
openPanel(PANEL_ID);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
client.addCommandCenterItem({
|
|
31
|
+
id: "beads-refresh-triage",
|
|
32
|
+
title: "Refresh Beads triage",
|
|
33
|
+
icon: "RefreshCw",
|
|
34
|
+
keywords: ["beads", "triage", "refresh", "bv"],
|
|
35
|
+
context: "workspace",
|
|
36
|
+
onSelect({ openPanel, workspace }) {
|
|
37
|
+
requestDashboardRefresh(workspace.id);
|
|
38
|
+
openPanel(PANEL_ID);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
client.addSlashCommand({
|
|
43
|
+
name: "beads",
|
|
44
|
+
description: "Open the read-only Beads console for this workspace",
|
|
45
|
+
argumentHint: "",
|
|
46
|
+
context: "workspace",
|
|
47
|
+
onSubmit({ openPanel }) {
|
|
48
|
+
openPanel(PANEL_ID);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
client.addSlashCommand({
|
|
53
|
+
name: "bead",
|
|
54
|
+
description: "Open the Beads console focused on one issue id",
|
|
55
|
+
argumentHint: "<issue-id>",
|
|
56
|
+
context: "workspace",
|
|
57
|
+
onSubmit({ args, openPanel, workspace }) {
|
|
58
|
+
const parsed = IssueIdSchema.safeParse(args.trim());
|
|
59
|
+
if (parsed.success) requestIssueFocus(workspace.id, parsed.data);
|
|
60
|
+
// Without a valid id the panel simply opens; nothing is mutated either way.
|
|
61
|
+
openPanel(PANEL_ID);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
client.addAttachmentSource(beadsAttachmentSource);
|
|
66
|
+
|
|
67
|
+
return () => {};
|
|
68
|
+
}
|
package/index.server.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
|
+
import { searchAttachments } from "./server/attachments";
|
|
3
|
+
import { killActiveProcesses } from "./server/command";
|
|
4
|
+
import { clearDashboardCache, getDashboard } from "./server/dashboard";
|
|
5
|
+
import { getIssue } from "./server/issue";
|
|
6
|
+
import { searchIssues } from "./server/search";
|
|
7
|
+
import { clearTrackerCache } from "./server/tracker";
|
|
8
|
+
import { attachmentSearchRpc, dashboardRpc, issueRpc, searchRpc } from "./shared/rpc";
|
|
9
|
+
|
|
10
|
+
export default function contribute(server: PluginServerContext) {
|
|
11
|
+
server.handle(dashboardRpc, getDashboard);
|
|
12
|
+
server.handle(searchRpc, searchIssues);
|
|
13
|
+
server.handle(issueRpc, getIssue);
|
|
14
|
+
server.handle(attachmentSearchRpc, searchAttachments);
|
|
15
|
+
|
|
16
|
+
return () => {
|
|
17
|
+
killActiveProcesses();
|
|
18
|
+
clearDashboardCache();
|
|
19
|
+
clearTrackerCache();
|
|
20
|
+
};
|
|
21
|
+
}
|