@skillcdn/console 0.1.14 → 0.1.15
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 +5 -4
- package/SKILLCDN.md +2 -0
- package/dist/agent-name.d.ts +3 -0
- package/dist/agent-name.d.ts.map +1 -0
- package/dist/agent-name.js +9 -0
- package/dist/agent-name.js.map +1 -0
- package/dist/api.d.ts +6 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +6 -4
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +215 -22
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +14 -4
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +59 -10
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +27 -11
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +5 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/client.d.ts +20 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +11 -2
- package/dist/client.js.map +1 -1
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +39 -29
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/connect.d.ts +4 -1
- package/dist/components/connect.d.ts.map +1 -1
- package/dist/components/connect.js +7 -3
- package/dist/components/connect.js.map +1 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +2 -2
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/documents.d.ts +5 -0
- package/dist/components/documents.d.ts.map +1 -1
- package/dist/components/documents.js +3 -2
- package/dist/components/documents.js.map +1 -1
- package/dist/components/event-feed.d.ts +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +10 -1
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/people.d.ts +24 -1
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +35 -3
- package/dist/components/people.js.map +1 -1
- package/dist/components/projects.d.ts +6 -0
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +3 -3
- package/dist/components/projects.js.map +1 -1
- package/dist/components/rules.d.ts +40 -0
- package/dist/components/rules.d.ts.map +1 -0
- package/dist/components/rules.js +46 -0
- package/dist/components/rules.js.map +1 -0
- package/dist/components/runs.d.ts +1 -1
- package/dist/components/runs.d.ts.map +1 -1
- package/dist/components/runs.js +2 -1
- package/dist/components/runs.js.map +1 -1
- package/dist/components/skills.d.ts.map +1 -1
- package/dist/components/skills.js +5 -1
- package/dist/components/skills.js.map +1 -1
- package/dist/components/tokens.d.ts.map +1 -1
- package/dist/components/tokens.js +2 -9
- package/dist/components/tokens.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 +13 -0
- package/dist/components/ui.js.map +1 -1
- package/dist/components/work.d.ts +5 -1
- package/dist/components/work.d.ts.map +1 -1
- package/dist/components/work.js +1 -1
- package/dist/components/work.js.map +1 -1
- package/dist/console.css +219 -13
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +197 -43
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +19 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +27 -1
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +83 -9
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +81 -11
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +81 -11
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +4 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +4 -0
- package/dist/limits.js.map +1 -1
- package/dist/router.d.ts +30 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +50 -10
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +14 -4
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +17 -2
- package/dist/routes.js.map +1 -1
- package/dist/rules.d.ts +28 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +102 -0
- package/dist/rules.js.map +1 -0
- package/dist/schemas.d.ts +652 -4
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +96 -6
- package/dist/schemas.js.map +1 -1
- package/dist/vocabulary.d.ts +21 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +21 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +13 -9
- package/docs/specs/rest.md +32 -8
- package/package.json +1 -1
- package/skills/building-a-console/SKILL.md +1 -1
- package/skills/deploying-a-console/SKILL.md +5 -5
- package/skills/deploying-a-console/assets/secrets.example.json +2 -1
- package/skills/deploying-a-console/references/access.md +10 -6
- package/skills/deploying-a-console/references/resources.md +10 -7
- package/skills/working-the-board/SKILL.md +10 -9
- package/skills/working-the-board/references/project-rules.md +36 -0
- package/src/agent-name.ts +9 -0
- package/src/api.ts +37 -0
- package/src/cli/cli.ts +253 -24
- package/src/cli/format.ts +74 -10
- package/src/cli/help.ts +30 -10
- package/src/cli/main.ts +5 -0
- package/src/client.ts +47 -1
- package/src/components/calendar.tsx +63 -50
- package/src/components/connect.tsx +12 -6
- package/src/components/decision-list.tsx +2 -1
- package/src/components/documents.tsx +7 -3
- package/src/components/event-feed.tsx +16 -3
- package/src/components/people.tsx +244 -35
- package/src/components/projects.tsx +31 -8
- package/src/components/rules.tsx +167 -0
- package/src/components/runs.tsx +4 -2
- package/src/components/skills.tsx +22 -10
- package/src/components/tokens.tsx +2 -14
- package/src/components/ui.tsx +25 -2
- package/src/components/work.tsx +34 -28
- package/src/console.tsx +523 -45
- package/src/data.ts +52 -2
- package/src/i18n/en.ts +91 -12
- package/src/i18n/ko.ts +87 -12
- package/src/index.ts +16 -1
- package/src/limits.ts +4 -0
- package/src/router.ts +67 -12
- package/src/routes.ts +23 -3
- package/src/rules.ts +109 -0
- package/src/schemas.ts +121 -4
- package/src/styles/console.css +219 -13
- package/src/vocabulary.ts +30 -0
package/src/client.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
categoryPath,
|
|
5
5
|
connectPath,
|
|
6
6
|
personTokensPath,
|
|
7
|
+
personWorkPath,
|
|
7
8
|
projectPath,
|
|
8
9
|
REST_ROUTES,
|
|
9
10
|
restPath,
|
|
11
|
+
rulesPath,
|
|
10
12
|
} from "./routes.js";
|
|
11
13
|
import {
|
|
12
14
|
type RestAnswerInput,
|
|
@@ -31,6 +33,8 @@ import {
|
|
|
31
33
|
type RestGoalInput,
|
|
32
34
|
type RestGoalPatch,
|
|
33
35
|
type RestGoals,
|
|
36
|
+
type RestListing,
|
|
37
|
+
type RestListingInput,
|
|
34
38
|
type RestMe,
|
|
35
39
|
type RestMember,
|
|
36
40
|
type RestMemberInput,
|
|
@@ -40,11 +44,15 @@ import {
|
|
|
40
44
|
type RestPeople,
|
|
41
45
|
type RestPerson,
|
|
42
46
|
type RestPersonPatch,
|
|
47
|
+
type RestPersonWork,
|
|
43
48
|
type RestProject,
|
|
44
49
|
type RestProjectInput,
|
|
45
50
|
type RestProjectPatch,
|
|
51
|
+
type RestProjectRules,
|
|
46
52
|
type RestProjects,
|
|
47
53
|
type RestReportInput,
|
|
54
|
+
type RestRules,
|
|
55
|
+
type RestRulesInput,
|
|
48
56
|
type RestRun,
|
|
49
57
|
type RestRunEndInput,
|
|
50
58
|
type RestRunInput,
|
|
@@ -73,13 +81,17 @@ import {
|
|
|
73
81
|
restEventsSchema,
|
|
74
82
|
restGoalSchema,
|
|
75
83
|
restGoalsSchema,
|
|
84
|
+
restListingSchema,
|
|
76
85
|
restMemberSchema,
|
|
77
86
|
restMembersSchema,
|
|
78
87
|
restMeSchema,
|
|
79
88
|
restPeopleSchema,
|
|
80
89
|
restPersonSchema,
|
|
90
|
+
restPersonWorkSchema,
|
|
91
|
+
restProjectRulesSchema,
|
|
81
92
|
restProjectSchema,
|
|
82
93
|
restProjectsSchema,
|
|
94
|
+
restRulesSchema,
|
|
83
95
|
restRunSchema,
|
|
84
96
|
restRunsSchema,
|
|
85
97
|
restSkillsSchema,
|
|
@@ -243,6 +255,8 @@ export interface ProjectClient {
|
|
|
243
255
|
eventStreamUrl(after: number): string;
|
|
244
256
|
/** The project's skills, as SkillCDN serves them at its address, or the organization's. */
|
|
245
257
|
skills(signal?: AbortSignal): Promise<RestSkills>;
|
|
258
|
+
/** The rules that hold in the project (ADR-0023): the workspace's, and what the project adds. */
|
|
259
|
+
rules(signal?: AbortSignal): Promise<RestProjectRules>;
|
|
246
260
|
/** A folder's pages and folders, or the pages a search finds. */
|
|
247
261
|
documents(filter?: DocumentFilter, signal?: AbortSignal): Promise<RestDocuments>;
|
|
248
262
|
/** One document, by its path: its latest version, its links both ways, its files. */
|
|
@@ -271,11 +285,17 @@ export interface ProjectClient {
|
|
|
271
285
|
|
|
272
286
|
export interface ConsoleClient {
|
|
273
287
|
me(signal?: AbortSignal): Promise<RestMe>;
|
|
288
|
+
/** Gives up the token this client acts with, so that it is good for nothing from now on: what `console logout` does. For a token; a session has none. */
|
|
289
|
+
giveUpToken(): Promise<void>;
|
|
274
290
|
/** Keeps the person's choice of language, or none; for a person signed in. */
|
|
275
291
|
updateMe(patch: RestMePatch): Promise<RestMe>;
|
|
276
292
|
people(signal?: AbortSignal): Promise<RestPeople>;
|
|
277
|
-
/** Changes what a person is; for an administrator. */
|
|
293
|
+
/** Changes what a person is, or whether they are in; for an administrator. */
|
|
278
294
|
updatePerson(id: string, patch: RestPersonPatch): Promise<RestPerson>;
|
|
295
|
+
/** Lets a login in (ADR-0022): whoever signs in with that account becomes a person; for an administrator. */
|
|
296
|
+
createListing(input: RestListingInput): Promise<RestListing>;
|
|
297
|
+
/** Takes a listing away before anyone signed in with it; for an administrator. */
|
|
298
|
+
removeListing(id: string): Promise<void>;
|
|
279
299
|
/** The projects the asker may see, with what they are in each. */
|
|
280
300
|
projects(signal?: AbortSignal): Promise<RestProjects>;
|
|
281
301
|
/** Makes a project, whose owner the asker becomes. */
|
|
@@ -286,6 +306,14 @@ export interface ConsoleClient {
|
|
|
286
306
|
events(after: number, signal?: AbortSignal): Promise<RestEvents>;
|
|
287
307
|
/** The categories tasks are sorted into (ADR-0019), by name. */
|
|
288
308
|
categories(signal?: AbortSignal): Promise<RestCategories>;
|
|
309
|
+
/** The workspace's rules (ADR-0023): the console's own until an administrator writes the organization's. */
|
|
310
|
+
rules(signal?: AbortSignal): Promise<RestRules>;
|
|
311
|
+
/** Writes the organization's rules; for an administrator. The console's own, sent as they are, write nothing. */
|
|
312
|
+
writeRules(input: RestRulesInput): Promise<RestRules>;
|
|
313
|
+
/** Puts the organization's rules away, so that the console's own hold again; for an administrator. */
|
|
314
|
+
archiveRules(): Promise<RestRules>;
|
|
315
|
+
/** The versions of the organization's rules, newest first; none while the console's own hold. */
|
|
316
|
+
rulesVersions(signal?: AbortSignal): Promise<RestVersions>;
|
|
289
317
|
/** Makes a category; for an administrator signed in. */
|
|
290
318
|
createCategory(input: RestCategoryInput): Promise<RestCategory>;
|
|
291
319
|
updateCategory(key: string, patch: RestCategoryPatch): Promise<RestCategory>;
|
|
@@ -293,6 +321,8 @@ export interface ConsoleClient {
|
|
|
293
321
|
removeCategory(key: string): Promise<void>;
|
|
294
322
|
/** What is on the asker's plate across the projects they may see: their open tasks, and the decisions waiting. */
|
|
295
323
|
work(signal?: AbortSignal): Promise<RestWork>;
|
|
324
|
+
/** Another person's plate, within what the asker may see: for an administrator, or an owner of a project they are in. */
|
|
325
|
+
personWork(personId: string, signal?: AbortSignal): Promise<RestPersonWork>;
|
|
296
326
|
/** The tokens of whoever asks. */
|
|
297
327
|
tokens(signal?: AbortSignal): Promise<RestTokens>;
|
|
298
328
|
/** The tokens of another person: for an administrator. */
|
|
@@ -520,6 +550,8 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
520
550
|
eventStreamUrl: (after) => withQuery(`${at("events")}/stream`, { after: String(after) }),
|
|
521
551
|
skills: (signal) =>
|
|
522
552
|
request("GET", `${base}${at("skills")}`, undefined, restSkillsSchema, signal),
|
|
553
|
+
rules: (signal) =>
|
|
554
|
+
request("GET", `${base}${at("rules")}`, undefined, restProjectRulesSchema, signal),
|
|
523
555
|
documents: (filter = {}, signal) =>
|
|
524
556
|
request(
|
|
525
557
|
"GET",
|
|
@@ -572,10 +604,15 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
572
604
|
|
|
573
605
|
return {
|
|
574
606
|
me: (signal) => request("GET", `${base}${REST_ROUTES.me}`, undefined, restMeSchema, signal),
|
|
607
|
+
giveUpToken: () => request("DELETE", `${base}${REST_ROUTES.meToken}`, undefined, undefined),
|
|
575
608
|
people: (signal) =>
|
|
576
609
|
request("GET", `${base}${REST_ROUTES.people}`, undefined, restPeopleSchema, signal),
|
|
577
610
|
updatePerson: (id, patch) =>
|
|
578
611
|
request("PATCH", `${base}${restPath("people", id)}`, patch, restPersonSchema),
|
|
612
|
+
createListing: (input) =>
|
|
613
|
+
request("POST", `${base}${REST_ROUTES.listings}`, input, restListingSchema),
|
|
614
|
+
removeListing: (id) =>
|
|
615
|
+
request("DELETE", `${base}${restPath("listings", id)}`, undefined, undefined),
|
|
579
616
|
projects: (signal) =>
|
|
580
617
|
request("GET", `${base}${REST_ROUTES.projects}`, undefined, restProjectsSchema, signal),
|
|
581
618
|
createProject: (input) =>
|
|
@@ -591,6 +628,13 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
591
628
|
),
|
|
592
629
|
categories: (signal) =>
|
|
593
630
|
request("GET", `${base}${REST_ROUTES.categories}`, undefined, restCategoriesSchema, signal),
|
|
631
|
+
rules: (signal) =>
|
|
632
|
+
request("GET", `${base}${REST_ROUTES.rules}`, undefined, restRulesSchema, signal),
|
|
633
|
+
writeRules: (input) => request("PUT", `${base}${REST_ROUTES.rules}`, input, restRulesSchema),
|
|
634
|
+
archiveRules: () =>
|
|
635
|
+
request("POST", `${base}${rulesPath("archive")}`, undefined, restRulesSchema),
|
|
636
|
+
rulesVersions: (signal) =>
|
|
637
|
+
request("GET", `${base}${rulesPath("versions")}`, undefined, restVersionsSchema, signal),
|
|
594
638
|
createCategory: (input) =>
|
|
595
639
|
request("POST", `${base}${REST_ROUTES.categories}`, input, restCategorySchema),
|
|
596
640
|
updateCategory: (key, patch) =>
|
|
@@ -598,6 +642,8 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
598
642
|
removeCategory: (key) => request("DELETE", `${base}${categoryPath(key)}`, undefined, undefined),
|
|
599
643
|
work: (signal) =>
|
|
600
644
|
request("GET", `${base}${REST_ROUTES.work}`, undefined, restWorkSchema, signal),
|
|
645
|
+
personWork: (personId, signal) =>
|
|
646
|
+
request("GET", `${base}${personWorkPath(personId)}`, undefined, restPersonWorkSchema, signal),
|
|
601
647
|
tokens: (signal) =>
|
|
602
648
|
request("GET", `${base}${REST_ROUTES.tokens}`, undefined, restTokensSchema, signal),
|
|
603
649
|
createToken: (input) =>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ReactNode, SyntheticEvent } from "react";
|
|
2
2
|
import { OPEN_TASK_STATES, type RestCategory, type RestPerson, type RestTask } from "../api.js";
|
|
3
3
|
import {
|
|
4
4
|
addDays,
|
|
@@ -16,14 +16,15 @@ import {
|
|
|
16
16
|
weekOf,
|
|
17
17
|
} from "../dates.js";
|
|
18
18
|
import { useLanguage, useWords } from "../i18n/index.js";
|
|
19
|
-
import { cx, PersonChip, personName } from "./ui.js";
|
|
19
|
+
import { Avatar, cx, PersonChip, personName } from "./ui.js";
|
|
20
20
|
|
|
21
21
|
// The calendar of a project (ADR-0019): the month as a grid, or one week on its own. A task
|
|
22
22
|
// planned from a start to a due day lies across its days as a bar, in a lane of its own within
|
|
23
|
-
// each week so that the bars of one week never cross; a task with
|
|
24
|
-
// a
|
|
25
|
-
//
|
|
26
|
-
//
|
|
23
|
+
// each week so that the bars of one week never cross; a task with one day alone, a due day or
|
|
24
|
+
// a start, sits on that day as a dot. On a project's calendar the person who has a task stands
|
|
25
|
+
// before its title, since the days hold several people's work. Who has what says whether the
|
|
26
|
+
// work is on its days. Takes its data as props and nothing from the network; the month or the
|
|
27
|
+
// week shown is the address's (ADR-0010).
|
|
27
28
|
|
|
28
29
|
const isOpen = (task: RestTask): boolean => OPEN_TASK_STATES.includes(task.state);
|
|
29
30
|
|
|
@@ -104,49 +105,67 @@ function push(map: Map<string, RestTask[]>, day: string, task: RestTask): void {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
/** The tasks that sit on one day each: due on it, or beginning on it with no due day yet. */
|
|
108
|
-
function onDays(tasks: readonly RestTask[]): {
|
|
109
|
-
|
|
110
|
-
readonly starting: ReadonlyMap<string, readonly RestTask[]>;
|
|
111
|
-
} {
|
|
112
|
-
const due = new Map<string, RestTask[]>();
|
|
113
|
-
const starting = new Map<string, RestTask[]>();
|
|
108
|
+
/** The tasks that sit on one day each, by day: due on it, or beginning on it with no due day yet. */
|
|
109
|
+
function onDays(tasks: readonly RestTask[]): ReadonlyMap<string, readonly RestTask[]> {
|
|
110
|
+
const on = new Map<string, RestTask[]>();
|
|
114
111
|
for (const task of tasks) {
|
|
115
112
|
if (spans(task)) {
|
|
116
113
|
continue;
|
|
117
114
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
push(starting, task.startsOn, task);
|
|
115
|
+
const day = task.dueOn ?? task.startsOn;
|
|
116
|
+
if (day !== null) {
|
|
117
|
+
push(on, day, task);
|
|
122
118
|
}
|
|
123
119
|
}
|
|
124
120
|
// Within a day, what is still open comes first; the rest reads as what was.
|
|
125
|
-
for (const list of
|
|
121
|
+
for (const list of on.values()) {
|
|
126
122
|
list.sort((a, b) => Number(isOpen(b)) - Number(isOpen(a)) || a.number - b.number);
|
|
127
123
|
}
|
|
128
|
-
return
|
|
124
|
+
return on;
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
/** How a task reads
|
|
132
|
-
const tone = (task: RestTask,
|
|
133
|
-
cx(
|
|
127
|
+
/** How a task reads: struck through once it is over, and marked once its due day is past. */
|
|
128
|
+
const tone = (task: RestTask, now: string): string =>
|
|
129
|
+
cx(
|
|
130
|
+
!isOpen(task) && "sc-calendar-over",
|
|
131
|
+
isOpen(task) && task.dueOn !== null && task.dueOn < now && "sc-calendar-late",
|
|
132
|
+
);
|
|
134
133
|
|
|
135
134
|
interface WeekRowProps {
|
|
136
135
|
readonly week: readonly GridDay[];
|
|
137
136
|
readonly segments: readonly WeekSegment[];
|
|
138
|
-
|
|
139
|
-
readonly
|
|
137
|
+
/** The tasks on one day each, by day. */
|
|
138
|
+
readonly on: ReadonlyMap<string, readonly RestTask[]>;
|
|
140
139
|
readonly now: string;
|
|
141
140
|
readonly taskHref: (task: RestTask) => string;
|
|
142
141
|
/** What stands before a task's title where the tasks are of several projects: the project's mark. */
|
|
143
142
|
readonly markOf?: ((task: RestTask) => ReactNode) | undefined;
|
|
143
|
+
/** Whether the person who has a task stands before its title, where the days hold several people's work. */
|
|
144
|
+
readonly people?: boolean | undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Marks an item whose title its line cut short, or which shows none (a bar on its later days),
|
|
149
|
+
* so that the tip it carries in `data-tip` is shown at once while the pointer is on it. Measured
|
|
150
|
+
* as the pointer arrives, since only the browser knows where the line ended.
|
|
151
|
+
*/
|
|
152
|
+
function reveal(event: SyntheticEvent<HTMLElement>): void {
|
|
153
|
+
const item = event.currentTarget;
|
|
154
|
+
const title = item.querySelector(".sc-calendar-task-title");
|
|
155
|
+
item.toggleAttribute("data-clipped", title === null || title.scrollWidth > title.clientWidth);
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
/** One week as a row of cells: the bars across the days in their lanes, then the tasks on each day. */
|
|
147
159
|
function WeekRow(props: WeekRowProps) {
|
|
148
|
-
const words = useWords().calendar;
|
|
149
160
|
const { week, segments, now } = props;
|
|
161
|
+
const tip = (task: RestTask): string =>
|
|
162
|
+
props.people === true && task.assignee !== null
|
|
163
|
+
? `#${task.number} ${task.title} \u00B7 ${personName(task.assignee)}`
|
|
164
|
+
: `#${task.number} ${task.title}`;
|
|
165
|
+
const who = (task: RestTask): ReactNode =>
|
|
166
|
+
props.people === true && task.assignee !== null ? (
|
|
167
|
+
<Avatar person={task.assignee} size="xs" />
|
|
168
|
+
) : null;
|
|
150
169
|
const lanes = segments.reduce((most, segment) => Math.max(most, segment.lane + 1), 0);
|
|
151
170
|
return (
|
|
152
171
|
<tr>
|
|
@@ -179,28 +198,34 @@ function WeekRow(props: WeekRowProps) {
|
|
|
179
198
|
`sc-category-${task.category?.color ?? "gray"}`,
|
|
180
199
|
segment.from === column && segment.begins && "sc-calendar-bar-begins",
|
|
181
200
|
segment.to === column && segment.ends && "sc-calendar-bar-ends",
|
|
182
|
-
tone(task,
|
|
201
|
+
tone(task, now),
|
|
202
|
+
"sc-tip",
|
|
183
203
|
)}
|
|
184
204
|
href={props.taskHref(task)}
|
|
185
|
-
|
|
205
|
+
data-tip={tip(task)}
|
|
206
|
+
onMouseEnter={reveal}
|
|
207
|
+
onFocus={reveal}
|
|
186
208
|
>
|
|
187
209
|
{segment.from === column && (
|
|
188
210
|
<>
|
|
189
211
|
{props.markOf?.(task)}
|
|
212
|
+
{who(task)}
|
|
190
213
|
<span className="sc-calendar-task-title">{task.title}</span>
|
|
191
214
|
</>
|
|
192
215
|
)}
|
|
193
216
|
</a>
|
|
194
217
|
);
|
|
195
218
|
})}
|
|
196
|
-
{(props.
|
|
219
|
+
{(props.on.get(cell.day) ?? []).map((task) => {
|
|
197
220
|
const mark = props.markOf?.(task);
|
|
198
221
|
return (
|
|
199
222
|
<a
|
|
200
223
|
key={task.id}
|
|
201
|
-
className={cx("sc-calendar-task", tone(task,
|
|
224
|
+
className={cx("sc-calendar-task", tone(task, now), "sc-tip")}
|
|
202
225
|
href={props.taskHref(task)}
|
|
203
|
-
|
|
226
|
+
data-tip={tip(task)}
|
|
227
|
+
onMouseEnter={reveal}
|
|
228
|
+
onFocus={reveal}
|
|
204
229
|
>
|
|
205
230
|
{mark === undefined || mark === null ? (
|
|
206
231
|
<span
|
|
@@ -212,21 +237,11 @@ function WeekRow(props: WeekRowProps) {
|
|
|
212
237
|
) : (
|
|
213
238
|
mark
|
|
214
239
|
)}
|
|
240
|
+
{who(task)}
|
|
215
241
|
<span className="sc-calendar-task-title">{task.title}</span>
|
|
216
242
|
</a>
|
|
217
243
|
);
|
|
218
244
|
})}
|
|
219
|
-
{(props.starting.get(cell.day) ?? []).map((task) => (
|
|
220
|
-
<a
|
|
221
|
-
key={task.id}
|
|
222
|
-
className="sc-calendar-task sc-calendar-task-start"
|
|
223
|
-
href={props.taskHref(task)}
|
|
224
|
-
title={`#${task.number} ${task.title}: ${words.startsOnly}`}
|
|
225
|
-
>
|
|
226
|
-
{props.markOf?.(task)}
|
|
227
|
-
<span className="sc-calendar-task-title">{task.title}</span>
|
|
228
|
-
</a>
|
|
229
|
-
))}
|
|
230
245
|
</td>
|
|
231
246
|
))}
|
|
232
247
|
</tr>
|
|
@@ -239,12 +254,11 @@ export function Calendar(props: CalendarProps) {
|
|
|
239
254
|
const now = props.today ?? todayOf();
|
|
240
255
|
const firstDay = firstDayOfWeek(tag);
|
|
241
256
|
const weeks = monthGrid(props.month, firstDay);
|
|
242
|
-
const
|
|
257
|
+
const on = onDays(props.tasks);
|
|
243
258
|
const inMonth = (day: string) => monthOf(day) === props.month;
|
|
244
259
|
const monthWeeks = weeks.map((week) => ({ week, segments: weekSegments(week, props.tasks) }));
|
|
245
260
|
const nothing =
|
|
246
|
-
[...
|
|
247
|
-
[...starting.keys()].every((day) => !inMonth(day)) &&
|
|
261
|
+
[...on.keys()].every((day) => !inMonth(day)) &&
|
|
248
262
|
monthWeeks.every(({ week, segments }) =>
|
|
249
263
|
segments.every(
|
|
250
264
|
(segment) => !week.slice(segment.from, segment.to + 1).some((cell) => cell.inMonth),
|
|
@@ -293,10 +307,10 @@ export function Calendar(props: CalendarProps) {
|
|
|
293
307
|
key={week[0]?.day}
|
|
294
308
|
week={week}
|
|
295
309
|
segments={segments}
|
|
296
|
-
|
|
297
|
-
starting={starting}
|
|
310
|
+
on={on}
|
|
298
311
|
now={now}
|
|
299
312
|
taskHref={props.taskHref}
|
|
313
|
+
people
|
|
300
314
|
/>
|
|
301
315
|
))}
|
|
302
316
|
</tbody>
|
|
@@ -341,9 +355,9 @@ export function Week(props: WeekProps) {
|
|
|
341
355
|
const first = days[0] ?? now;
|
|
342
356
|
const last = days[days.length - 1] ?? now;
|
|
343
357
|
const week: GridDay[] = days.map((day) => ({ day, inMonth: true }));
|
|
344
|
-
const
|
|
358
|
+
const on = onDays(props.tasks);
|
|
345
359
|
const segments = weekSegments(week, props.tasks);
|
|
346
|
-
const nothing = segments.length === 0 && days.every((day) => !
|
|
360
|
+
const nothing = segments.length === 0 && days.every((day) => !on.has(day));
|
|
347
361
|
return (
|
|
348
362
|
<div className="sc-calendar sc-week">
|
|
349
363
|
<div className="sc-calendar-head">
|
|
@@ -382,8 +396,7 @@ export function Week(props: WeekProps) {
|
|
|
382
396
|
<WeekRow
|
|
383
397
|
week={week}
|
|
384
398
|
segments={segments}
|
|
385
|
-
|
|
386
|
-
starting={starting}
|
|
399
|
+
on={on}
|
|
387
400
|
now={now}
|
|
388
401
|
taskHref={props.taskHref}
|
|
389
402
|
markOf={props.markOf}
|
|
@@ -2,7 +2,7 @@ import { useState } from "react";
|
|
|
2
2
|
import { normalizeConnectCode, type RestConnectRequest, type RestTokenInput } from "../api.js";
|
|
3
3
|
import { useWords } from "../i18n/index.js";
|
|
4
4
|
import { TokenForm } from "./tokens.js";
|
|
5
|
-
import { Button, Callout, Time } from "./ui.js";
|
|
5
|
+
import { Button, Callout, CopyButton, Time } from "./ui.js";
|
|
6
6
|
|
|
7
7
|
// Connecting an agent (ADR-0011): where a person types the code their agent showed, what asks
|
|
8
8
|
// to connect under a code with the way to approve it, and the words that tell a person what to
|
|
@@ -106,16 +106,22 @@ export interface ConnectWordsProps {
|
|
|
106
106
|
readonly origin: string;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* What a person says to their agent to connect it: the words to paste into its chat, as they
|
|
111
|
+
* are, with the way to copy them; and the commands for a person who runs them by hand.
|
|
112
|
+
*/
|
|
110
113
|
export function ConnectWords(props: ConnectWordsProps) {
|
|
111
114
|
const words = useWords().connect.words;
|
|
115
|
+
const quote = words.quote(props.origin);
|
|
112
116
|
return (
|
|
113
117
|
<section className="sc-panel sc-connect" aria-label={words.title}>
|
|
114
118
|
<h2 className="sc-section-title">{words.title}</h2>
|
|
115
|
-
<p>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
<p>{words.tellYourAgent}</p>
|
|
120
|
+
<div className="sc-copyable">
|
|
121
|
+
<pre className="sc-code">{quote}</pre>
|
|
122
|
+
<CopyButton text={quote} size="sm" />
|
|
123
|
+
</div>
|
|
124
|
+
<p>{words.doesTheRest}</p>
|
|
119
125
|
<p>{words.byHand}</p>
|
|
120
126
|
<pre className="sc-code">
|
|
121
127
|
npm install -g @skillcdn/console{"\n"}console login --url {props.origin}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import {
|
|
3
|
+
agentLabel,
|
|
3
4
|
MAX_BODY_LENGTH,
|
|
4
5
|
MAX_NOTE_LENGTH,
|
|
5
6
|
type RestAnswerInput,
|
|
@@ -128,7 +129,7 @@ export function DecisionCard(props: DecisionCardProps) {
|
|
|
128
129
|
<PersonChip person={decision.raisedBy} />{" "}
|
|
129
130
|
{decision.run === null
|
|
130
131
|
? words.decisions.asked
|
|
131
|
-
: words.decisions.askedThrough(decision.run.agent)}{" "}
|
|
132
|
+
: words.decisions.askedThrough(agentLabel(decision.run.agent))}{" "}
|
|
132
133
|
<Time iso={decision.createdAt} />
|
|
133
134
|
{task !== undefined && (
|
|
134
135
|
<>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FormEvent, type ReactNode, useState } from "react";
|
|
2
|
+
import { agentLabel } from "../agent-name.js";
|
|
2
3
|
import {
|
|
3
4
|
foldersAbove,
|
|
4
5
|
isDocumentPath,
|
|
@@ -59,7 +60,7 @@ export function DocumentCrumbs(props: {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/** Who wrote a version, and through which agent, as a chip and a word. */
|
|
62
|
-
function Writer(props: {
|
|
63
|
+
export function Writer(props: {
|
|
63
64
|
readonly by: RestDocumentSummary["updatedBy"];
|
|
64
65
|
readonly agent: string | null;
|
|
65
66
|
}) {
|
|
@@ -68,8 +69,11 @@ function Writer(props: {
|
|
|
68
69
|
<>
|
|
69
70
|
<PersonChip person={props.by} />
|
|
70
71
|
{props.agent !== null && (
|
|
71
|
-
<span
|
|
72
|
-
|
|
72
|
+
<span
|
|
73
|
+
className="sc-feed-agent"
|
|
74
|
+
title={agentLabel(props.agent) === props.agent ? words.docs.asAgentTitle : props.agent}
|
|
75
|
+
>
|
|
76
|
+
{words.feed.as(agentLabel(props.agent))}
|
|
73
77
|
</span>
|
|
74
78
|
)}
|
|
75
79
|
</>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { agentLabel, type RestEvent } from "../api.js";
|
|
3
3
|
import { en, type Messages } from "../i18n/en.js";
|
|
4
4
|
import { useWords } from "../i18n/index.js";
|
|
5
5
|
import { ShowMore, usePaged } from "./paged.js";
|
|
@@ -38,6 +38,14 @@ export function describeEvent(event: RestEvent, words: Messages = en): string {
|
|
|
38
38
|
return feed.event.personJoined();
|
|
39
39
|
case "person.role_changed":
|
|
40
40
|
return feed.event.roleChanged(login, role(data.role));
|
|
41
|
+
case "person.listed":
|
|
42
|
+
return feed.event.personListed(login);
|
|
43
|
+
case "person.unlisted":
|
|
44
|
+
return feed.event.personUnlisted(login);
|
|
45
|
+
case "person.deactivated":
|
|
46
|
+
return feed.event.personDeactivated(login);
|
|
47
|
+
case "person.reactivated":
|
|
48
|
+
return feed.event.personReactivated(login);
|
|
41
49
|
case "project.created":
|
|
42
50
|
return feed.event.projectCreated(project);
|
|
43
51
|
case "project.updated":
|
|
@@ -129,8 +137,13 @@ export function EventFeed(props: EventFeedProps) {
|
|
|
129
137
|
<PersonChip person={event.actor} />
|
|
130
138
|
)}
|
|
131
139
|
{event.agent !== null && (
|
|
132
|
-
<span
|
|
133
|
-
|
|
140
|
+
<span
|
|
141
|
+
className="sc-feed-agent"
|
|
142
|
+
title={
|
|
143
|
+
agentLabel(event.agent) === event.agent ? words.feed.asTitle : event.agent
|
|
144
|
+
}
|
|
145
|
+
>
|
|
146
|
+
{words.feed.as(agentLabel(event.agent))}
|
|
134
147
|
</span>
|
|
135
148
|
)}
|
|
136
149
|
</span>
|