@skillcdn/console 0.1.10 → 0.1.12
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/README.md +6 -5
- package/dist/api.d.ts +6 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -4
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +49 -9
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +2 -0
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +7 -6
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +10 -9
- package/dist/cli/help.js.map +1 -1
- package/dist/client.d.ts +14 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +13 -1
- package/dist/client.js.map +1 -1
- package/dist/components/board.d.ts +1 -4
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +6 -13
- package/dist/components/board.js.map +1 -1
- package/dist/components/calendar.d.ts +21 -1
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +69 -9
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +11 -9
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +11 -7
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/paged.d.ts +14 -0
- package/dist/components/paged.d.ts.map +1 -0
- package/dist/components/paged.js +28 -0
- package/dist/components/paged.js.map +1 -0
- package/dist/components/people.js +2 -2
- package/dist/components/people.js.map +1 -1
- package/dist/components/projects.d.ts +22 -2
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +37 -9
- package/dist/components/projects.js.map +1 -1
- package/dist/components/runs.d.ts.map +1 -1
- package/dist/components/runs.js +3 -3
- package/dist/components/runs.js.map +1 -1
- package/dist/components/shell.d.ts +2 -1
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +3 -3
- package/dist/components/shell.js.map +1 -1
- package/dist/components/task-form.js +2 -2
- package/dist/components/task-form.js.map +1 -1
- package/dist/components/task-list.d.ts +1 -1
- package/dist/components/task-list.d.ts.map +1 -1
- package/dist/components/task-list.js +6 -4
- package/dist/components/task-list.js.map +1 -1
- package/dist/components/task-view.js +1 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts +7 -2
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +9 -4
- package/dist/components/ui.js.map +1 -1
- package/dist/components/work.d.ts.map +1 -1
- package/dist/components/work.js +7 -3
- package/dist/components/work.js.map +1 -1
- package/dist/console.css +149 -2
- package/dist/console.d.ts +38 -4
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +84 -23
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +5 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +15 -2
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +17 -11
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +18 -14
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +18 -14
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +6 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +6 -0
- package/dist/limits.js.map +1 -1
- package/dist/marks.d.ts +12 -0
- package/dist/marks.d.ts.map +1 -0
- package/dist/marks.js +31 -0
- package/dist/marks.js.map +1 -0
- package/dist/router.d.ts +19 -3
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +28 -5
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +7 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +8 -0
- package/dist/routes.js.map +1 -1
- package/dist/schemas.d.ts +123 -3
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +42 -10
- package/dist/schemas.js.map +1 -1
- package/dist/vocabulary.d.ts +10 -4
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +8 -3
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +7 -5
- package/docs/specs/rest.md +18 -12
- package/package.json +1 -1
- package/skills/building-a-console/SKILL.md +2 -2
- package/skills/working-the-board/SKILL.md +1 -1
- package/src/api.ts +12 -0
- package/src/cli/cli.ts +51 -8
- package/src/cli/format.ts +8 -6
- package/src/cli/help.ts +10 -10
- package/src/client.ts +29 -6
- package/src/components/board.tsx +6 -30
- package/src/components/calendar.tsx +168 -56
- package/src/components/decision-list.tsx +38 -33
- package/src/components/event-feed.tsx +34 -27
- package/src/components/paged.tsx +39 -0
- package/src/components/people.tsx +5 -5
- package/src/components/projects.tsx +132 -12
- package/src/components/runs.tsx +9 -3
- package/src/components/shell.tsx +8 -5
- package/src/components/task-form.tsx +2 -2
- package/src/components/task-list.tsx +35 -32
- package/src/components/task-view.tsx +1 -1
- package/src/components/ui.tsx +14 -5
- package/src/components/work.tsx +12 -2
- package/src/console.tsx +158 -25
- package/src/data.ts +20 -4
- package/src/i18n/en.ts +19 -14
- package/src/i18n/ko.ts +19 -14
- package/src/index.ts +9 -0
- package/src/limits.ts +6 -0
- package/src/marks.ts +40 -0
- package/src/router.ts +45 -7
- package/src/routes.ts +9 -0
- package/src/schemas.ts +48 -8
- package/src/styles/console.css +149 -2
- package/src/vocabulary.ts +14 -4
package/src/index.ts
CHANGED
|
@@ -16,9 +16,11 @@ export {
|
|
|
16
16
|
export {
|
|
17
17
|
Calendar,
|
|
18
18
|
type CalendarProps,
|
|
19
|
+
type WeekSegment,
|
|
19
20
|
Workload,
|
|
20
21
|
type WorkloadProps,
|
|
21
22
|
type WorkloadRow,
|
|
23
|
+
weekSegments,
|
|
22
24
|
workloadOf,
|
|
23
25
|
} from "./components/calendar.js";
|
|
24
26
|
export { CategoryList, type CategoryListProps } from "./components/categories.js";
|
|
@@ -64,6 +66,7 @@ export {
|
|
|
64
66
|
type GoalViewProps,
|
|
65
67
|
} from "./components/goals.js";
|
|
66
68
|
export { Markdown, type MarkdownProps } from "./components/markdown.js";
|
|
69
|
+
export { PAGE_SIZE, ShowMore, usePaged } from "./components/paged.js";
|
|
67
70
|
export { PeopleList, type PeopleListProps } from "./components/people.js";
|
|
68
71
|
export {
|
|
69
72
|
keyOf,
|
|
@@ -74,6 +77,7 @@ export {
|
|
|
74
77
|
type ProjectFormProps,
|
|
75
78
|
ProjectList,
|
|
76
79
|
type ProjectListProps,
|
|
80
|
+
ProjectMark,
|
|
77
81
|
ProjectRoleBadge,
|
|
78
82
|
VISIBILITY_LABELS,
|
|
79
83
|
} from "./components/projects.js";
|
|
@@ -123,6 +127,7 @@ export {
|
|
|
123
127
|
PRIORITY_LABELS,
|
|
124
128
|
PriorityBadge,
|
|
125
129
|
ProgressBar,
|
|
130
|
+
personName,
|
|
126
131
|
ROLE_LABELS,
|
|
127
132
|
RoleBadge,
|
|
128
133
|
Spinner,
|
|
@@ -136,7 +141,10 @@ export {
|
|
|
136
141
|
type ConsoleApp,
|
|
137
142
|
type ConsoleComponents,
|
|
138
143
|
type ConsoleConfig,
|
|
144
|
+
type CustomPage,
|
|
145
|
+
type CustomPageContext,
|
|
139
146
|
createConsole,
|
|
147
|
+
customPageLabel,
|
|
140
148
|
DEFAULT_COMPONENTS,
|
|
141
149
|
} from "./console.js";
|
|
142
150
|
export { type ConsoleActions, type ConsoleData, useConsoleData } from "./data.js";
|
|
@@ -176,6 +184,7 @@ export {
|
|
|
176
184
|
boardHref,
|
|
177
185
|
calendarHref,
|
|
178
186
|
connectHref,
|
|
187
|
+
customPageHref,
|
|
179
188
|
type DocsView,
|
|
180
189
|
decisionHref,
|
|
181
190
|
docHref,
|
package/src/limits.ts
CHANGED
|
@@ -64,3 +64,9 @@ export const MAX_CATEGORY_KEY_LENGTH = 40;
|
|
|
64
64
|
export const MAX_CATEGORY_NAME_LENGTH = 60;
|
|
65
65
|
/** How many categories one workspace may have: a vocabulary, not a tag cloud. */
|
|
66
66
|
export const MAX_CATEGORIES = 50;
|
|
67
|
+
/** A project's mark: a letter or two, or a symbol, shown on its colour; counted in code units. */
|
|
68
|
+
export const MAX_PROJECT_SYMBOL_LENGTH = 8;
|
|
69
|
+
/** How large a project's own image for its mark may be, in bytes. */
|
|
70
|
+
export const MAX_PROJECT_ICON_BYTES = 512 * 1024;
|
|
71
|
+
/** How many items a listing answers with when none is asked for, and the most one may ask for. */
|
|
72
|
+
export const DEFAULT_PAGE_LIMIT = 50;
|
package/src/marks.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { COLOR_NAMES, type ColorName } from "./vocabulary.js";
|
|
2
|
+
|
|
3
|
+
// A project's mark: a symbol on a colour, so that projects tell apart at a glance. A project
|
|
4
|
+
// that names neither gets a colour from its key, the same everywhere and every time, and the
|
|
5
|
+
// first letter of its name.
|
|
6
|
+
|
|
7
|
+
/** A small, well-spread hash of a string: enough to pick a colour, nothing more. */
|
|
8
|
+
function hashOf(value: string): number {
|
|
9
|
+
let hash = 2166136261;
|
|
10
|
+
for (const char of value) {
|
|
11
|
+
hash ^= char.codePointAt(0) ?? 0;
|
|
12
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
13
|
+
}
|
|
14
|
+
return hash;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The colour of a project's mark: the one it names, else one chosen from its key. */
|
|
18
|
+
export function projectColorOf(project: {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly color: ColorName | null;
|
|
21
|
+
}): ColorName {
|
|
22
|
+
if (project.color !== null) {
|
|
23
|
+
return project.color;
|
|
24
|
+
}
|
|
25
|
+
// Gray is for what has no colour; a project chosen one gets one of the rest.
|
|
26
|
+
const choices = COLOR_NAMES.filter((name) => name !== "gray");
|
|
27
|
+
return choices[hashOf(project.key) % choices.length] ?? "gray";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The symbol of a project's mark: the one it names, else the first letter of its name. */
|
|
31
|
+
export function projectSymbolOf(project: {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly symbol: string | null;
|
|
34
|
+
}): string {
|
|
35
|
+
if (project.symbol !== null && project.symbol.length > 0) {
|
|
36
|
+
return project.symbol;
|
|
37
|
+
}
|
|
38
|
+
const first = [...project.name.trim()][0];
|
|
39
|
+
return first === undefined ? "?" : first.toUpperCase();
|
|
40
|
+
}
|
package/src/router.ts
CHANGED
|
@@ -52,8 +52,13 @@ export type Route =
|
|
|
52
52
|
| { readonly name: "decisions"; readonly project: string }
|
|
53
53
|
| { readonly name: "new-decision"; readonly project: string }
|
|
54
54
|
| { readonly name: "decision"; readonly project: string; readonly id: string }
|
|
55
|
-
/** The calendar of a project: the month shown, or this one when the address names none. */
|
|
56
|
-
| {
|
|
55
|
+
/** The calendar of a project: the month shown, or this one when the address names none, narrowed as the board is. */
|
|
56
|
+
| {
|
|
57
|
+
readonly name: "calendar";
|
|
58
|
+
readonly project: string;
|
|
59
|
+
readonly month: string | undefined;
|
|
60
|
+
readonly filter: BoardFilter;
|
|
61
|
+
}
|
|
57
62
|
| { readonly name: "roadmap"; readonly project: string }
|
|
58
63
|
| { readonly name: "new-goal"; readonly project: string }
|
|
59
64
|
| {
|
|
@@ -79,6 +84,14 @@ export type Route =
|
|
|
79
84
|
| { readonly name: "connect"; readonly code: string | undefined }
|
|
80
85
|
/** An address that moved: the page takes the person to `to` in its place. */
|
|
81
86
|
| { readonly name: "moved"; readonly to: string }
|
|
87
|
+
/** A page a console adds of its own (`createConsole({ pages })`): on the workspace, or on a project. */
|
|
88
|
+
| { readonly name: "custom"; readonly level: "workspace"; readonly page: string }
|
|
89
|
+
| {
|
|
90
|
+
readonly name: "custom";
|
|
91
|
+
readonly level: "project";
|
|
92
|
+
readonly project: string;
|
|
93
|
+
readonly page: string;
|
|
94
|
+
}
|
|
82
95
|
| { readonly name: "not-found" };
|
|
83
96
|
|
|
84
97
|
/** The pages of a project, after its key. */
|
|
@@ -107,6 +120,8 @@ export const PATHS = {
|
|
|
107
120
|
newToken: "/agents/new",
|
|
108
121
|
/** The categories tasks are sorted into, kept by an administrator. */
|
|
109
122
|
categories: "/categories",
|
|
123
|
+
/** Where the pages a console adds of its own are: `/pages/<key>`, and `/projects/<key>/pages/<page>` on a project. */
|
|
124
|
+
pages: "/pages",
|
|
110
125
|
/** Where an agent's connection is approved (ADR-0011): `/connect/<code>`, or the code typed. */
|
|
111
126
|
connect: "/connect",
|
|
112
127
|
} as const;
|
|
@@ -218,6 +233,12 @@ export function matchRoute(pathname: string, search = ""): Route {
|
|
|
218
233
|
? { name: "person-agents", id }
|
|
219
234
|
: NOT_FOUND;
|
|
220
235
|
}
|
|
236
|
+
if (path.startsWith(`${PATHS.pages}/`)) {
|
|
237
|
+
const [page, ...more] = path.slice(PATHS.pages.length + 1).split("/");
|
|
238
|
+
return page !== undefined && PROJECT_KEY.test(page) && more.length === 0
|
|
239
|
+
? { name: "custom", level: "workspace", page }
|
|
240
|
+
: NOT_FOUND;
|
|
241
|
+
}
|
|
221
242
|
if (!path.startsWith(`${PATHS.project}/`)) {
|
|
222
243
|
return NOT_FOUND;
|
|
223
244
|
}
|
|
@@ -237,6 +258,11 @@ export function matchRoute(pathname: string, search = ""): Route {
|
|
|
237
258
|
const view = docsViewOf(params, docPath);
|
|
238
259
|
return view === undefined ? NOT_FOUND : { name: "docs", project, path: docPath, view };
|
|
239
260
|
}
|
|
261
|
+
if (page === "pages") {
|
|
262
|
+
return third !== undefined && PROJECT_KEY.test(third) && rest.length === 0
|
|
263
|
+
? { name: "custom", level: "project", project, page: third }
|
|
264
|
+
: NOT_FOUND;
|
|
265
|
+
}
|
|
240
266
|
if (rest.length > 0) {
|
|
241
267
|
return NOT_FOUND;
|
|
242
268
|
}
|
|
@@ -280,7 +306,7 @@ export function matchRoute(pathname: string, search = ""): Route {
|
|
|
280
306
|
if (month !== null && !isMonth(month)) {
|
|
281
307
|
return NOT_FOUND;
|
|
282
308
|
}
|
|
283
|
-
return { name: "calendar", project, month: month ?? undefined };
|
|
309
|
+
return { name: "calendar", project, month: month ?? undefined, filter: boardFilterOf(params) };
|
|
284
310
|
}
|
|
285
311
|
if (page === "roadmap") {
|
|
286
312
|
return { name: "roadmap", project };
|
|
@@ -307,6 +333,12 @@ export function projectHref(key: string, page?: ProjectPage): string {
|
|
|
307
333
|
|
|
308
334
|
/** Where a project's board is, narrowed as the filter says; the plain board for an empty one. */
|
|
309
335
|
export function boardHref(key: string, filter: BoardFilter = {}): string {
|
|
336
|
+
const parts = filterParts(filter);
|
|
337
|
+
return parts.length === 0 ? projectHref(key) : `${projectHref(key)}?${parts.join("&")}`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** The filter as the query carries it, a part per narrowing; nothing for the plain board. */
|
|
341
|
+
function filterParts(filter: BoardFilter): string[] {
|
|
310
342
|
const parts: string[] = [];
|
|
311
343
|
// An empty value means "none": it keeps its sign; "mine" is a flag and has no value.
|
|
312
344
|
if (filter.category !== undefined) {
|
|
@@ -321,7 +353,7 @@ export function boardHref(key: string, filter: BoardFilter = {}): string {
|
|
|
321
353
|
if (filter.mine === true) {
|
|
322
354
|
parts.push("mine");
|
|
323
355
|
}
|
|
324
|
-
return parts
|
|
356
|
+
return parts;
|
|
325
357
|
}
|
|
326
358
|
|
|
327
359
|
/** Where a task is written: the form. */
|
|
@@ -345,10 +377,16 @@ export function decisionHref(key: string, id: string): string {
|
|
|
345
377
|
return `${PATHS.project}/${key}/decisions/${id}`;
|
|
346
378
|
}
|
|
347
379
|
|
|
348
|
-
/** Where a project's calendar is, on a month, or on this one when none is named. */
|
|
349
|
-
export function calendarHref(key: string, month?: string): string {
|
|
380
|
+
/** Where a project's calendar is, on a month, or on this one when none is named, narrowed as the filter says. */
|
|
381
|
+
export function calendarHref(key: string, month?: string, filter: BoardFilter = {}): string {
|
|
350
382
|
const base = projectHref(key, "calendar");
|
|
351
|
-
|
|
383
|
+
const parts = [...(month === undefined ? [] : [`month=${month}`]), ...filterParts(filter)];
|
|
384
|
+
return parts.length === 0 ? base : `${base}?${parts.join("&")}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Where a page a console adds of its own is: on the workspace, or on a project when its key is given. */
|
|
388
|
+
export function customPageHref(page: string, project?: string): string {
|
|
389
|
+
return project === undefined ? `${PATHS.pages}/${page}` : `${projectHref(project)}/pages/${page}`;
|
|
352
390
|
}
|
|
353
391
|
|
|
354
392
|
/** Where a goal is set: the form. */
|
package/src/routes.ts
CHANGED
|
@@ -114,3 +114,12 @@ export function projectPath(key: string, collection?: ProjectCollection, id?: st
|
|
|
114
114
|
? `${base}/${collection}`
|
|
115
115
|
: `${base}/${collection}/${encodeURIComponent(id)}`;
|
|
116
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Where a project's own image for its mark is read, with the hash as a version where the image
|
|
120
|
+
* is known, so that a browser's copy is asked for again only when the image changes.
|
|
121
|
+
*/
|
|
122
|
+
export function projectIconPath(key: string, icon?: { readonly sha256: string } | null): string {
|
|
123
|
+
const base = `${projectPath(key)}/icon`;
|
|
124
|
+
return icon === undefined || icon === null ? base : `${base}?v=${icon.sha256.slice(0, 16)}`;
|
|
125
|
+
}
|
package/src/schemas.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MAX_PROJECT_DESCRIPTION_LENGTH,
|
|
22
22
|
MAX_PROJECT_KEY_LENGTH,
|
|
23
23
|
MAX_PROJECT_NAME_LENGTH,
|
|
24
|
+
MAX_PROJECT_SYMBOL_LENGTH,
|
|
24
25
|
MAX_QUESTION_LENGTH,
|
|
25
26
|
MAX_SECRET_LENGTH,
|
|
26
27
|
MAX_SKILLS_ADDRESS_LENGTH,
|
|
@@ -35,10 +36,12 @@ import { hasForbiddenCodePoint } from "./text.js";
|
|
|
35
36
|
import {
|
|
36
37
|
ARTIFACT_KINDS,
|
|
37
38
|
CATEGORY_COLORS,
|
|
39
|
+
COLOR_NAMES,
|
|
38
40
|
EVENT_KINDS,
|
|
39
41
|
GOAL_STATES,
|
|
40
42
|
LINK_SOURCES,
|
|
41
43
|
PERSON_ROLES,
|
|
44
|
+
PROJECT_ICON_TYPES,
|
|
42
45
|
PROJECT_ROLES,
|
|
43
46
|
PROJECT_VISIBILITIES,
|
|
44
47
|
PROVIDER_KEYS,
|
|
@@ -160,7 +163,19 @@ const projectKey = z.string().check(
|
|
|
160
163
|
/** The address of a project's skills, as the standard spells one; the console checks it as one. */
|
|
161
164
|
const skillsAddress = line(MAX_SKILLS_ADDRESS_LENGTH);
|
|
162
165
|
|
|
166
|
+
/** A project's mark: a letter or two, or a symbol, with no space in it; shown on the project's colour. */
|
|
167
|
+
const projectSymbol = line(MAX_PROJECT_SYMBOL_LENGTH).check(
|
|
168
|
+
z.refine((value) => !/\s/.test(value), "must have no space in it"),
|
|
169
|
+
);
|
|
170
|
+
|
|
163
171
|
/** A project of the workspace, as the asker sees it: with what they are in it. */
|
|
172
|
+
/** A project's own image for its mark: the hash it is kept by, which `projectIconPath` reads it with, and its kind. */
|
|
173
|
+
export const restProjectIconSchema = z.object({
|
|
174
|
+
sha256: z.string().check(z.regex(/^[0-9a-f]{64}$/)),
|
|
175
|
+
contentType: z.enum(PROJECT_ICON_TYPES),
|
|
176
|
+
});
|
|
177
|
+
export type RestProjectIcon = z.infer<typeof restProjectIconSchema>;
|
|
178
|
+
|
|
164
179
|
export const restProjectSchema = z.object({
|
|
165
180
|
id: uuid,
|
|
166
181
|
key: projectKey,
|
|
@@ -170,6 +185,12 @@ export const restProjectSchema = z.object({
|
|
|
170
185
|
visibility: z.enum(PROJECT_VISIBILITIES),
|
|
171
186
|
/** The address of the project's skills at the SkillCDN deployment, canonical, or `null` for the organization's. */
|
|
172
187
|
skillsAddress: z.nullable(z.string()),
|
|
188
|
+
/** The colour of the project's mark, one of the names, or `null` for one chosen from its key. */
|
|
189
|
+
color: z.nullable(z.enum(COLOR_NAMES)),
|
|
190
|
+
/** The project's mark, a letter or two or a symbol, or `null` for the first letter of its name. */
|
|
191
|
+
symbol: z.nullable(z.string()),
|
|
192
|
+
/** The project's own image for its mark, shown in place of the symbol, or `null` for none. */
|
|
193
|
+
icon: z.nullable(restProjectIconSchema),
|
|
173
194
|
/** What the asker is in it: an owner configures the project, a member works on it. */
|
|
174
195
|
role: z.enum(PROJECT_ROLES),
|
|
175
196
|
/** How many decisions in it wait for a person. */
|
|
@@ -194,6 +215,9 @@ export const restProjectInputSchema = z.object({
|
|
|
194
215
|
visibility: z.optional(z.enum(PROJECT_VISIBILITIES)),
|
|
195
216
|
/** Left out or `null`: the organization's skills. */
|
|
196
217
|
skillsAddress: z.optional(z.nullable(skillsAddress)),
|
|
218
|
+
/** Left out or `null`: a colour chosen from the key, and the first letter of the name. */
|
|
219
|
+
color: z.optional(z.nullable(z.enum(COLOR_NAMES))),
|
|
220
|
+
symbol: z.optional(z.nullable(projectSymbol)),
|
|
197
221
|
});
|
|
198
222
|
export type RestProjectInput = z.infer<typeof restProjectInputSchema>;
|
|
199
223
|
|
|
@@ -203,6 +227,8 @@ export const restProjectPatchSchema = z.object({
|
|
|
203
227
|
description: z.optional(paragraphs(MAX_PROJECT_DESCRIPTION_LENGTH)),
|
|
204
228
|
visibility: z.optional(z.enum(PROJECT_VISIBILITIES)),
|
|
205
229
|
skillsAddress: z.optional(z.nullable(skillsAddress)),
|
|
230
|
+
color: z.optional(z.nullable(z.enum(COLOR_NAMES))),
|
|
231
|
+
symbol: z.optional(z.nullable(projectSymbol)),
|
|
206
232
|
});
|
|
207
233
|
export type RestProjectPatch = z.infer<typeof restProjectPatchSchema>;
|
|
208
234
|
|
|
@@ -313,8 +339,8 @@ export const restTaskSchema = z.object({
|
|
|
313
339
|
});
|
|
314
340
|
export type RestTask = z.infer<typeof restTaskSchema>;
|
|
315
341
|
|
|
316
|
-
/** `GET /api/v1/projects/<key>/tasks`: the board, newest first. */
|
|
317
|
-
export const restTasksSchema = z.object({ items: z.array(restTaskSchema) });
|
|
342
|
+
/** `GET /api/v1/projects/<key>/tasks`: a page of the board, newest first, and whether there is more after it. */
|
|
343
|
+
export const restTasksSchema = z.object({ items: z.array(restTaskSchema), more: z.boolean() });
|
|
318
344
|
export type RestTasks = z.infer<typeof restTasksSchema>;
|
|
319
345
|
|
|
320
346
|
const taskLinkInput = z.object({
|
|
@@ -449,12 +475,21 @@ export const restDecisionSchema = z.object({
|
|
|
449
475
|
});
|
|
450
476
|
export type RestDecision = z.infer<typeof restDecisionSchema>;
|
|
451
477
|
|
|
452
|
-
/** `GET /api/v1/projects/<key>/decisions`: the ones that wait first, newest first within each. */
|
|
453
|
-
export const restDecisionsSchema = z.object({
|
|
478
|
+
/** `GET /api/v1/projects/<key>/decisions`: a page, the ones that wait first, newest first within each, and whether there is more. */
|
|
479
|
+
export const restDecisionsSchema = z.object({
|
|
480
|
+
items: z.array(restDecisionSchema),
|
|
481
|
+
more: z.boolean(),
|
|
482
|
+
});
|
|
454
483
|
export type RestDecisions = z.infer<typeof restDecisionsSchema>;
|
|
455
484
|
|
|
456
|
-
/** The project a thing on a person's plate belongs to, named for the page to say and
|
|
457
|
-
const projectRef = z.object({
|
|
485
|
+
/** The project a thing on a person's plate belongs to, named and marked for the page to say, link and show it. */
|
|
486
|
+
const projectRef = z.object({
|
|
487
|
+
key: projectKey,
|
|
488
|
+
name: z.string(),
|
|
489
|
+
color: z.nullable(z.enum(COLOR_NAMES)),
|
|
490
|
+
symbol: z.nullable(z.string()),
|
|
491
|
+
icon: z.nullable(restProjectIconSchema),
|
|
492
|
+
});
|
|
458
493
|
|
|
459
494
|
/**
|
|
460
495
|
* `GET /api/v1/work`: what is on the asker's plate across the projects they may see (ADR-0019):
|
|
@@ -598,6 +633,11 @@ export const restRunSchema = z.object({
|
|
|
598
633
|
status: z.enum(RUN_STATUSES),
|
|
599
634
|
startedAt: instant,
|
|
600
635
|
endedAt: z.nullable(instant),
|
|
636
|
+
/**
|
|
637
|
+
* When the agent was last heard from on this run: when it began, reported, handed in or
|
|
638
|
+
* asked. The board cannot know whether an agent still runs; this is what it knows.
|
|
639
|
+
*/
|
|
640
|
+
lastHeardAt: instant,
|
|
601
641
|
/** What the agent said when the run ended, in Markdown, or `null`. */
|
|
602
642
|
summary: z.nullable(z.string()),
|
|
603
643
|
/** Oldest first. */
|
|
@@ -608,8 +648,8 @@ export const restRunSchema = z.object({
|
|
|
608
648
|
});
|
|
609
649
|
export type RestRun = z.infer<typeof restRunSchema>;
|
|
610
650
|
|
|
611
|
-
/** `GET /api/v1/projects/<key>/runs`: the runs, newest first. */
|
|
612
|
-
export const restRunsSchema = z.object({ items: z.array(restRunSchema) });
|
|
651
|
+
/** `GET /api/v1/projects/<key>/runs`: a page of the runs, newest first, and whether there is more. */
|
|
652
|
+
export const restRunsSchema = z.object({ items: z.array(restRunSchema), more: z.boolean() });
|
|
613
653
|
export type RestRuns = z.infer<typeof restRunsSchema>;
|
|
614
654
|
|
|
615
655
|
/** `GET .../events?after=`: what happened after that number, oldest first. */
|
package/src/styles/console.css
CHANGED
|
@@ -1197,6 +1197,7 @@ table {
|
|
|
1197
1197
|
flex: 1 1 0;
|
|
1198
1198
|
grid-auto-flow: column;
|
|
1199
1199
|
grid-auto-columns: minmax(var(--sc-column-width), 1fr);
|
|
1200
|
+
grid-template-rows: minmax(0, 1fr);
|
|
1200
1201
|
gap: var(--sc-space-3);
|
|
1201
1202
|
min-height: 24rem;
|
|
1202
1203
|
overflow-x: auto;
|
|
@@ -1209,6 +1210,8 @@ table {
|
|
|
1209
1210
|
display: flex;
|
|
1210
1211
|
flex-direction: column;
|
|
1211
1212
|
min-height: 0;
|
|
1213
|
+
/* The body scrolls; the column clips, so that nothing of it reaches the board's own scrolling. */
|
|
1214
|
+
overflow: hidden;
|
|
1212
1215
|
border-radius: var(--sc-radius-xl);
|
|
1213
1216
|
scroll-snap-align: start;
|
|
1214
1217
|
}
|
|
@@ -2575,15 +2578,89 @@ table {
|
|
|
2575
2578
|
white-space: nowrap;
|
|
2576
2579
|
}
|
|
2577
2580
|
|
|
2578
|
-
.sc-calendar-
|
|
2581
|
+
.sc-calendar-over {
|
|
2579
2582
|
color: var(--sc-color-text-faint);
|
|
2580
2583
|
text-decoration: line-through;
|
|
2581
2584
|
}
|
|
2582
2585
|
|
|
2583
|
-
.sc-calendar-
|
|
2586
|
+
.sc-calendar-late {
|
|
2584
2587
|
color: var(--sc-color-danger);
|
|
2585
2588
|
}
|
|
2586
2589
|
|
|
2590
|
+
/* A task planned across days lies across them: a bar in a lane of its own, in its category's
|
|
2591
|
+
colour, reaching over the gap between the cells so that it reads as one. */
|
|
2592
|
+
.sc-calendar-lane,
|
|
2593
|
+
.sc-calendar-bar {
|
|
2594
|
+
display: flex;
|
|
2595
|
+
align-items: center;
|
|
2596
|
+
height: 1.125rem;
|
|
2597
|
+
margin-top: 2px;
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
.sc-calendar-bar {
|
|
2601
|
+
margin-inline: calc(-1 * var(--sc-space-2) - 0.125rem - 1px);
|
|
2602
|
+
padding-inline: var(--sc-space-2);
|
|
2603
|
+
border-block: 1px solid color-mix(in srgb, var(--sc-category-line) 45%, transparent);
|
|
2604
|
+
background: var(--sc-category-fill);
|
|
2605
|
+
color: var(--sc-category-line);
|
|
2606
|
+
font-size: var(--sc-text-xs);
|
|
2607
|
+
line-height: 1;
|
|
2608
|
+
text-decoration: none;
|
|
2609
|
+
overflow: hidden;
|
|
2610
|
+
white-space: nowrap;
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
.sc-calendar-bar:hover {
|
|
2614
|
+
filter: brightness(1.15);
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
.sc-calendar-bar-begins {
|
|
2618
|
+
margin-left: 0;
|
|
2619
|
+
border-left: 1px solid color-mix(in srgb, var(--sc-category-line) 45%, transparent);
|
|
2620
|
+
border-start-start-radius: var(--sc-radius-full);
|
|
2621
|
+
border-end-start-radius: var(--sc-radius-full);
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
.sc-calendar-bar-ends {
|
|
2625
|
+
margin-right: 0;
|
|
2626
|
+
border-right: 1px solid color-mix(in srgb, var(--sc-category-line) 45%, transparent);
|
|
2627
|
+
border-start-end-radius: var(--sc-radius-full);
|
|
2628
|
+
border-end-end-radius: var(--sc-radius-full);
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
.sc-calendar-bar.sc-calendar-late {
|
|
2632
|
+
border-color: color-mix(in srgb, var(--sc-color-danger) 70%, transparent);
|
|
2633
|
+
color: var(--sc-color-danger);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
.sc-calendar-bar.sc-calendar-over {
|
|
2637
|
+
opacity: 0.55;
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
.sc-calendar-legend {
|
|
2641
|
+
display: flex;
|
|
2642
|
+
flex-wrap: wrap;
|
|
2643
|
+
gap: var(--sc-space-1) var(--sc-space-4);
|
|
2644
|
+
margin: 0;
|
|
2645
|
+
color: var(--sc-color-text-muted);
|
|
2646
|
+
font-size: var(--sc-text-xs);
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
.sc-calendar-legend div {
|
|
2650
|
+
display: flex;
|
|
2651
|
+
align-items: center;
|
|
2652
|
+
gap: var(--sc-space-1);
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
.sc-calendar-legend dt {
|
|
2656
|
+
display: flex;
|
|
2657
|
+
align-items: center;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
.sc-calendar-legend dd {
|
|
2661
|
+
margin: 0;
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2587
2664
|
.sc-calendar-task-start {
|
|
2588
2665
|
color: var(--sc-color-text-muted);
|
|
2589
2666
|
}
|
|
@@ -2593,6 +2670,69 @@ table {
|
|
|
2593
2670
|
color: var(--sc-color-text-faint);
|
|
2594
2671
|
}
|
|
2595
2672
|
|
|
2673
|
+
/* The button under a list shown a page at a time. */
|
|
2674
|
+
.sc-show-more {
|
|
2675
|
+
margin-top: var(--sc-space-3);
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
/* A project's mark: its symbol on its colour. */
|
|
2679
|
+
.sc-mark {
|
|
2680
|
+
display: inline-flex;
|
|
2681
|
+
flex: none;
|
|
2682
|
+
align-items: center;
|
|
2683
|
+
justify-content: center;
|
|
2684
|
+
border-radius: var(--sc-radius-sm);
|
|
2685
|
+
background: var(--sc-category-fill);
|
|
2686
|
+
color: var(--sc-category-line);
|
|
2687
|
+
font-weight: var(--sc-weight-bold);
|
|
2688
|
+
line-height: 1;
|
|
2689
|
+
vertical-align: middle;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
.sc-mark-sm {
|
|
2693
|
+
width: 1.125rem;
|
|
2694
|
+
height: 1.125rem;
|
|
2695
|
+
margin-right: var(--sc-space-1);
|
|
2696
|
+
font-size: 0.625rem;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
.sc-mark-md {
|
|
2700
|
+
width: 2rem;
|
|
2701
|
+
height: 2rem;
|
|
2702
|
+
border-radius: var(--sc-radius-md);
|
|
2703
|
+
font-size: var(--sc-text-sm);
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
.sc-mark-lg {
|
|
2707
|
+
width: 3rem;
|
|
2708
|
+
height: 3rem;
|
|
2709
|
+
border-radius: var(--sc-radius-lg);
|
|
2710
|
+
font-size: var(--sc-text-xl);
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
.sc-mark-row {
|
|
2714
|
+
align-items: end;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
/* A project's own image, in place of the symbol, filling the mark. */
|
|
2718
|
+
.sc-mark-pictured {
|
|
2719
|
+
padding: 0;
|
|
2720
|
+
overflow: hidden;
|
|
2721
|
+
background: var(--sc-color-bg-inset);
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
.sc-mark-image {
|
|
2725
|
+
display: block;
|
|
2726
|
+
width: 100%;
|
|
2727
|
+
height: 100%;
|
|
2728
|
+
object-fit: cover;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
.sc-input-file {
|
|
2732
|
+
padding-block: calc(var(--sc-space-2) - 2px);
|
|
2733
|
+
font-size: var(--sc-text-sm);
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2596
2736
|
.sc-calendar-below {
|
|
2597
2737
|
display: grid;
|
|
2598
2738
|
align-items: start;
|
|
@@ -2921,6 +3061,13 @@ table {
|
|
|
2921
3061
|
display: none;
|
|
2922
3062
|
}
|
|
2923
3063
|
|
|
3064
|
+
.sc-calendar-lane,
|
|
3065
|
+
.sc-calendar-bar {
|
|
3066
|
+
height: 4px;
|
|
3067
|
+
padding: 0;
|
|
3068
|
+
border: 0;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
2924
3071
|
.sc-feed-item {
|
|
2925
3072
|
grid-template-columns: 1fr;
|
|
2926
3073
|
gap: var(--sc-space-1);
|
package/src/vocabulary.ts
CHANGED
|
@@ -31,10 +31,10 @@ export const GOAL_STATES = ["planned", "active", "done", "dropped"] as const;
|
|
|
31
31
|
export type GoalState = (typeof GOAL_STATES)[number];
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* The colours a category may be shown in: names, so that a custom console
|
|
35
|
-
* own tokens and no value
|
|
34
|
+
* The colours a category, or a project's mark, may be shown in: names, so that a custom console
|
|
35
|
+
* paints them in its own tokens and no value a person typed reaches a stylesheet.
|
|
36
36
|
*/
|
|
37
|
-
export const
|
|
37
|
+
export const COLOR_NAMES = [
|
|
38
38
|
"gray",
|
|
39
39
|
"blue",
|
|
40
40
|
"green",
|
|
@@ -45,7 +45,16 @@ export const CATEGORY_COLORS = [
|
|
|
45
45
|
"yellow",
|
|
46
46
|
] as const;
|
|
47
47
|
|
|
48
|
-
export type
|
|
48
|
+
export type ColorName = (typeof COLOR_NAMES)[number];
|
|
49
|
+
|
|
50
|
+
/** The colours of the categories: the same names. */
|
|
51
|
+
export const CATEGORY_COLORS = COLOR_NAMES;
|
|
52
|
+
|
|
53
|
+
export type CategoryColor = ColorName;
|
|
54
|
+
|
|
55
|
+
/** The kinds of image a project's own mark may be: what a browser shows in place, and nothing that can run. */
|
|
56
|
+
export const PROJECT_ICON_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"] as const;
|
|
57
|
+
export type ProjectIconType = (typeof PROJECT_ICON_TYPES)[number];
|
|
49
58
|
|
|
50
59
|
/** The identity providers people may sign in through: the git host, and Google (Workspace). */
|
|
51
60
|
export const PROVIDER_KEYS = ["gh", "google"] as const;
|
|
@@ -132,6 +141,7 @@ export const EVENT_KINDS = [
|
|
|
132
141
|
"run.started",
|
|
133
142
|
"run.reported",
|
|
134
143
|
"run.handed_in",
|
|
144
|
+
"run.taken_over",
|
|
135
145
|
"run.ended",
|
|
136
146
|
"document.written",
|
|
137
147
|
"document.archived",
|