@skillcdn/console 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +4 -4
  2. package/dist/api.d.ts +4 -4
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +4 -4
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli/cli.d.ts +5 -1
  7. package/dist/cli/cli.d.ts.map +1 -1
  8. package/dist/cli/cli.js +290 -46
  9. package/dist/cli/cli.js.map +1 -1
  10. package/dist/cli/credentials.d.ts +5 -0
  11. package/dist/cli/credentials.d.ts.map +1 -1
  12. package/dist/cli/credentials.js +2 -0
  13. package/dist/cli/credentials.js.map +1 -1
  14. package/dist/cli/directory.d.ts +7 -0
  15. package/dist/cli/directory.d.ts.map +1 -1
  16. package/dist/cli/directory.js +7 -0
  17. package/dist/cli/directory.js.map +1 -1
  18. package/dist/cli/format.d.ts +10 -1
  19. package/dist/cli/format.d.ts.map +1 -1
  20. package/dist/cli/format.js +76 -20
  21. package/dist/cli/format.js.map +1 -1
  22. package/dist/cli/help.d.ts.map +1 -1
  23. package/dist/cli/help.js +42 -23
  24. package/dist/cli/help.js.map +1 -1
  25. package/dist/cli/main.js +1 -0
  26. package/dist/cli/main.js.map +1 -1
  27. package/dist/client.d.ts +39 -9
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +24 -3
  30. package/dist/client.js.map +1 -1
  31. package/dist/compat.d.ts +28 -6
  32. package/dist/compat.d.ts.map +1 -1
  33. package/dist/compat.js +55 -14
  34. package/dist/compat.js.map +1 -1
  35. package/dist/components/board.d.ts.map +1 -1
  36. package/dist/components/board.js +6 -4
  37. package/dist/components/board.js.map +1 -1
  38. package/dist/components/calendar.d.ts +18 -8
  39. package/dist/components/calendar.d.ts.map +1 -1
  40. package/dist/components/calendar.js +39 -27
  41. package/dist/components/calendar.js.map +1 -1
  42. package/dist/components/event-feed.d.ts.map +1 -1
  43. package/dist/components/event-feed.js +2 -0
  44. package/dist/components/event-feed.js.map +1 -1
  45. package/dist/components/pack-connect.d.ts +23 -0
  46. package/dist/components/pack-connect.d.ts.map +1 -0
  47. package/dist/components/pack-connect.js +86 -0
  48. package/dist/components/pack-connect.js.map +1 -0
  49. package/dist/components/pack-token.d.ts +27 -0
  50. package/dist/components/pack-token.d.ts.map +1 -0
  51. package/dist/components/pack-token.js +11 -0
  52. package/dist/components/pack-token.js.map +1 -0
  53. package/dist/components/picker.d.ts +39 -0
  54. package/dist/components/picker.d.ts.map +1 -0
  55. package/dist/components/picker.js +155 -0
  56. package/dist/components/picker.js.map +1 -0
  57. package/dist/components/projects.d.ts +13 -0
  58. package/dist/components/projects.d.ts.map +1 -1
  59. package/dist/components/projects.js +37 -5
  60. package/dist/components/projects.js.map +1 -1
  61. package/dist/components/task-form.d.ts +18 -4
  62. package/dist/components/task-form.d.ts.map +1 -1
  63. package/dist/components/task-form.js +56 -13
  64. package/dist/components/task-form.js.map +1 -1
  65. package/dist/components/task-list.js +2 -2
  66. package/dist/components/task-list.js.map +1 -1
  67. package/dist/components/task-view.d.ts +7 -1
  68. package/dist/components/task-view.d.ts.map +1 -1
  69. package/dist/components/task-view.js +6 -5
  70. package/dist/components/task-view.js.map +1 -1
  71. package/dist/components/ui.d.ts +7 -0
  72. package/dist/components/ui.d.ts.map +1 -1
  73. package/dist/components/ui.js +11 -0
  74. package/dist/components/ui.js.map +1 -1
  75. package/dist/components/work.d.ts +3 -1
  76. package/dist/components/work.d.ts.map +1 -1
  77. package/dist/components/work.js +6 -4
  78. package/dist/components/work.js.map +1 -1
  79. package/dist/components/workspace.d.ts +46 -0
  80. package/dist/components/workspace.d.ts.map +1 -0
  81. package/dist/components/workspace.js +124 -0
  82. package/dist/components/workspace.js.map +1 -0
  83. package/dist/console.css +319 -11
  84. package/dist/console.d.ts.map +1 -1
  85. package/dist/console.js +83 -24
  86. package/dist/console.js.map +1 -1
  87. package/dist/data.d.ts +26 -1
  88. package/dist/data.d.ts.map +1 -1
  89. package/dist/data.js +59 -1
  90. package/dist/data.js.map +1 -1
  91. package/dist/dates.d.ts +12 -2
  92. package/dist/dates.d.ts.map +1 -1
  93. package/dist/dates.js +20 -2
  94. package/dist/dates.js.map +1 -1
  95. package/dist/i18n/en.d.ts +64 -3
  96. package/dist/i18n/en.d.ts.map +1 -1
  97. package/dist/i18n/en.js +81 -16
  98. package/dist/i18n/en.js.map +1 -1
  99. package/dist/i18n/ko.d.ts.map +1 -1
  100. package/dist/i18n/ko.js +77 -16
  101. package/dist/i18n/ko.js.map +1 -1
  102. package/dist/index.d.ts +6 -2
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +6 -2
  105. package/dist/index.js.map +1 -1
  106. package/dist/limits.d.ts +2 -0
  107. package/dist/limits.d.ts.map +1 -1
  108. package/dist/limits.js +2 -0
  109. package/dist/limits.js.map +1 -1
  110. package/dist/router.d.ts +4 -4
  111. package/dist/router.d.ts.map +1 -1
  112. package/dist/router.js +11 -2
  113. package/dist/router.js.map +1 -1
  114. package/dist/routes.d.ts +4 -0
  115. package/dist/routes.d.ts.map +1 -1
  116. package/dist/routes.js +4 -0
  117. package/dist/routes.js.map +1 -1
  118. package/dist/schemas.d.ts +194 -13
  119. package/dist/schemas.d.ts.map +1 -1
  120. package/dist/schemas.js +121 -21
  121. package/dist/schemas.js.map +1 -1
  122. package/dist/vocabulary.d.ts +21 -1
  123. package/dist/vocabulary.d.ts.map +1 -1
  124. package/dist/vocabulary.js +18 -0
  125. package/dist/vocabulary.js.map +1 -1
  126. package/docs/specs/cli.md +25 -20
  127. package/docs/specs/rest.md +42 -19
  128. package/package.json +2 -2
  129. package/skills/building-a-console/SKILL.md +6 -6
  130. package/skills/building-a-console/references/the-look-from-a-website.md +39 -0
  131. package/skills/deploying-a-console/SKILL.md +9 -8
  132. package/skills/deploying-a-console/references/resources.md +1 -1
  133. package/skills/working-the-board/SKILL.md +5 -5
  134. package/skills/working-the-board/references/setting-up.md +1 -1
  135. package/skills/working-the-board/references/terms.md +3 -1
  136. package/skills/working-the-board/references/the-sentence.md +1 -1
  137. package/src/api.ts +21 -0
  138. package/src/cli/cli.ts +384 -51
  139. package/src/cli/credentials.ts +7 -0
  140. package/src/cli/directory.ts +9 -0
  141. package/src/cli/format.ts +83 -20
  142. package/src/cli/help.ts +50 -23
  143. package/src/cli/main.ts +1 -0
  144. package/src/client.ts +107 -10
  145. package/src/compat.ts +63 -14
  146. package/src/components/board.tsx +10 -4
  147. package/src/components/calendar.tsx +76 -34
  148. package/src/components/event-feed.tsx +2 -0
  149. package/src/components/pack-connect.tsx +179 -0
  150. package/src/components/pack-token.tsx +74 -0
  151. package/src/components/picker.tsx +297 -0
  152. package/src/components/projects.tsx +108 -9
  153. package/src/components/task-form.tsx +149 -63
  154. package/src/components/task-list.tsx +2 -2
  155. package/src/components/task-view.tsx +23 -9
  156. package/src/components/ui.tsx +25 -0
  157. package/src/components/work.tsx +10 -5
  158. package/src/components/workspace.tsx +298 -0
  159. package/src/console.tsx +202 -32
  160. package/src/data.ts +101 -1
  161. package/src/dates.ts +23 -2
  162. package/src/i18n/en.ts +92 -16
  163. package/src/i18n/ko.ts +84 -16
  164. package/src/index.ts +32 -1
  165. package/src/limits.ts +2 -0
  166. package/src/router.ts +17 -5
  167. package/src/routes.ts +4 -0
  168. package/src/schemas.ts +140 -19
  169. package/src/styles/console.css +319 -11
  170. package/src/vocabulary.ts +28 -0
package/src/schemas.ts CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  MAX_TOKEN_DAYS,
40
40
  MAX_TOKEN_NAME_LENGTH,
41
41
  MAX_URL_LENGTH,
42
+ MAX_WORKERS,
42
43
  MIN_OPTIONS,
43
44
  } from "./limits.js";
44
45
  import { hasForbiddenCodePoint } from "./text.js";
@@ -50,6 +51,8 @@ import {
50
51
  GOAL_STATES,
51
52
  LINK_SOURCES,
52
53
  MEMBERSHIP_SOURCES,
54
+ PACK_CONNECT_ENDINGS,
55
+ PEOPLE_VISIBILITIES,
53
56
  PERSON_ROLES,
54
57
  PERSON_STATUSES,
55
58
  PROJECT_ICON_TYPES,
@@ -63,6 +66,7 @@ import {
63
66
  SKILLS_STATUSES,
64
67
  TASK_PRIORITIES,
65
68
  TASK_STATES,
69
+ WEEK_STARTS,
66
70
  } from "./vocabulary.js";
67
71
 
68
72
  /** A line of text a person wrote: trimmed, bounded, and free of characters that hide. */
@@ -166,6 +170,18 @@ export const restProviderSchema = z.object({
166
170
  });
167
171
  export type RestProvider = z.infer<typeof restProviderSchema>;
168
172
 
173
+ /**
174
+ * A time zone's name as the IANA database spells one (`Asia/Seoul`, `UTC`): its shape here; the
175
+ * server knows which names are zones.
176
+ */
177
+ export const timeZoneName = z
178
+ .string()
179
+ .check(
180
+ z.minLength(1),
181
+ z.maxLength(64),
182
+ z.regex(/^[A-Za-z0-9_+-]+(?:\/[A-Za-z0-9_+-]+)*$/, "must be a time zone name"),
183
+ );
184
+
169
185
  /** A language tag as a browser sends one (`en`, `ko-KR`): what the pages speak, by the person's choice. */
170
186
  const languageTag = z
171
187
  .string()
@@ -178,22 +194,67 @@ const languageTag = z
178
194
  export const restMePatchSchema = z.object({ language: z.nullable(languageTag) });
179
195
  export type RestMePatch = z.infer<typeof restMePatchSchema>;
180
196
 
197
+ /**
198
+ * The workspace as `GET /api/v1/me` describes it, and as `PATCH /api/v1/workspace` answers: what
199
+ * the board is called, what the deployment's configuration says of it, and the settings an
200
+ * administrator keeps on the Settings page (ADR-0035).
201
+ */
202
+ export const restWorkspaceSchema = z.object({
203
+ name: z.string(),
204
+ /** The most days a token may be good for here, or `null` when a person chooses. */
205
+ tokenDaysAtMost: z.nullable(z.int().check(z.positive())),
206
+ /**
207
+ * The organization's skills: the origin of the SkillCDN deployment they are read through,
208
+ * and the address a project without one of its own reads, canonical, or `null` for none.
209
+ */
210
+ skills: z.object({
211
+ source: z.string(),
212
+ address: z.nullable(z.string()),
213
+ /**
214
+ * The SkillCDN token the organization's pack is read with, by a project that names no
215
+ * address and holds no token of its own (ADR-0036): when it was set, never the token; `null`
216
+ * for none. Optional, so that a console older than it still answers.
217
+ */
218
+ packToken: z.optional(z.nullable(z.object({ setAt: instant }))),
219
+ }),
220
+ /**
221
+ * Who sees whom on the People page (ADR-0035): everyone, or the people one shares a project
222
+ * with. Read as optional, so that a console older than this setting still answers; `everyone`
223
+ * then, which is what it does.
224
+ */
225
+ peopleVisibility: z.optional(z.enum(PEOPLE_VISIBILITIES)),
226
+ /** The day the calendars begin a week on: the language's, Monday or Sunday. Optional as above; `auto` then. */
227
+ weekStart: z.optional(z.enum(WEEK_STARTS)),
228
+ /**
229
+ * The workspace's time zone, an IANA name, in which the command prints moments; `null` for
230
+ * none, when they stay as the API gives them, in UTC. Optional as above; none then.
231
+ */
232
+ timeZone: z.optional(z.nullable(timeZoneName)),
233
+ });
234
+ export type RestWorkspace = z.infer<typeof restWorkspaceSchema>;
235
+
236
+ /** What `PATCH /api/v1/workspace` is sent, by an administrator: the settings to change, at least one. */
237
+ export const restWorkspacePatchSchema = z
238
+ .object({
239
+ peopleVisibility: z.optional(z.enum(PEOPLE_VISIBILITIES)),
240
+ weekStart: z.optional(z.enum(WEEK_STARTS)),
241
+ /** A zone's name, or `null` for none. */
242
+ timeZone: z.optional(z.nullable(timeZoneName)),
243
+ })
244
+ .check(
245
+ z.refine((patch) => Object.values(patch).some((value) => value !== undefined), {
246
+ message: "names nothing to change",
247
+ }),
248
+ );
249
+ export type RestWorkspacePatch = z.infer<typeof restWorkspacePatchSchema>;
250
+
181
251
  /**
182
252
  * `GET /api/v1/me`: the workspace, who the session cookie says is signed in (or `null`), the
183
253
  * language they chose (or `null`), and the identity providers people sign in through, none
184
254
  * where nobody can.
185
255
  */
186
256
  export const restMeSchema = z.object({
187
- workspace: z.object({
188
- name: z.string(),
189
- /** The most days a token may be good for here, or `null` when a person chooses. */
190
- tokenDaysAtMost: z.nullable(z.int().check(z.positive())),
191
- /**
192
- * The organization's skills: the origin of the SkillCDN deployment they are read through,
193
- * and the address a project without one of its own reads, canonical, or `null` for none.
194
- */
195
- skills: z.object({ source: z.string(), address: z.nullable(z.string()) }),
196
- }),
257
+ workspace: restWorkspaceSchema,
197
258
  person: z.nullable(restPersonSchema),
198
259
  /**
199
260
  * What the token asking is called by its person, when a token asks: how a console of a
@@ -329,15 +390,25 @@ export const restProjectPatchSchema = z.object({
329
390
  });
330
391
  export type RestProjectPatch = z.infer<typeof restProjectPatchSchema>;
331
392
 
332
- /** A person listed in a project, with what they are in it. */
393
+ /**
394
+ * A member of a project, with what they are in it: a person listed in it, or, in a project open
395
+ * to the workspace, anyone of the workspace, who is a member of it without a listing (ADR-0035).
396
+ */
333
397
  export const restMemberSchema = z.object({
334
398
  person: restPersonSchema,
399
+ /** What they are in it: an owner as listed or as an administrator, else a member. */
335
400
  role: z.enum(PROJECT_ROLES),
401
+ /** Since when: the listing's day, or, for a member without one, the later of the project's making and their joining. */
336
402
  addedAt: instant,
403
+ /**
404
+ * Whether a listing holds them in the project. Read as optional, so that a console older than
405
+ * this field still answers; everyone it lists is listed.
406
+ */
407
+ listed: z.optional(z.boolean()),
337
408
  });
338
409
  export type RestMember = z.infer<typeof restMemberSchema>;
339
410
 
340
- /** `GET /api/v1/projects/<key>/members`: those listed, by login. */
411
+ /** `GET /api/v1/projects/<key>/members`: the members, by login; everyone of the workspace in a project open to it. */
341
412
  export const restMembersSchema = z.object({ items: z.array(restMemberSchema) });
342
413
  export type RestMembers = z.infer<typeof restMembersSchema>;
343
414
 
@@ -410,8 +481,10 @@ export const restTaskSchema = z.object({
410
481
  body: z.string(),
411
482
  state: z.enum(TASK_STATES),
412
483
  priority: z.enum(TASK_PRIORITIES),
484
+ /** Whose task it is: one person, whoever wrote it. */
413
485
  owner: restPersonSchema,
414
- assignee: z.nullable(restPersonSchema),
486
+ /** The people at work on it (ADR-0037), in the order they came to it; `[]` while nobody is. */
487
+ workers: z.array(restPersonSchema),
415
488
  /** The task this one is part of, or `null`. */
416
489
  parentId: z.nullable(uuid),
417
490
  /** The category the task is sorted into, as the workspace keeps it, or `null`. */
@@ -460,7 +533,8 @@ export const restTaskInputSchema = z.object({
460
533
  body: z.optional(body),
461
534
  state: z.optional(z.enum(TASK_STATES)),
462
535
  priority: z.optional(z.enum(TASK_PRIORITIES)),
463
- assigneeId: z.optional(z.nullable(uuid)),
536
+ /** The people to be at work on it, each one who may work in the project, in place of those there were; `[]` for nobody. */
537
+ workerIds: z.optional(z.array(uuid).check(z.maxLength(MAX_WORKERS))),
464
538
  parentId: z.optional(z.nullable(uuid)),
465
539
  /** The key of a category of the workspace, or `null` for none. */
466
540
  category: z.optional(z.nullable(categoryKey)),
@@ -479,7 +553,7 @@ export const restTaskPatchSchema = z.object({
479
553
  body: z.optional(body),
480
554
  state: z.optional(z.enum(TASK_STATES)),
481
555
  priority: z.optional(z.enum(TASK_PRIORITIES)),
482
- assigneeId: z.optional(z.nullable(uuid)),
556
+ workerIds: z.optional(z.array(uuid).check(z.maxLength(MAX_WORKERS))),
483
557
  parentId: z.optional(z.nullable(uuid)),
484
558
  category: z.optional(z.nullable(categoryKey)),
485
559
  goalId: z.optional(z.nullable(uuid)),
@@ -526,8 +600,8 @@ export const restGoalSchema = z.object({
526
600
  });
527
601
  export type RestGoal = z.infer<typeof restGoalSchema>;
528
602
 
529
- /** `GET /api/v1/projects/<key>/goals`: the roadmap, the active goals first. */
530
- export const restGoalsSchema = z.object({ items: z.array(restGoalSchema) });
603
+ /** `GET /api/v1/projects/<key>/goals`: a page of the roadmap, the active goals first, and whether there is more after it. */
604
+ export const restGoalsSchema = z.object({ items: z.array(restGoalSchema), more: z.boolean() });
531
605
  export type RestGoals = z.infer<typeof restGoalsSchema>;
532
606
 
533
607
  /** What `POST /api/v1/projects/<key>/goals` is sent: the title, and the rest with defaults. */
@@ -616,8 +690,8 @@ const projectRef = z.object({
616
690
 
617
691
  /**
618
692
  * `GET /api/v1/work`: what is on the asker's plate across the projects they may see (ADR-0019):
619
- * the open tasks assigned to them, and the decisions that wait for a person, each with its
620
- * project; and, for the week on the plate, `closed`, the tasks assigned to them that are done
693
+ * the open tasks they work on, and the decisions that wait for a person, each with its
694
+ * project; and, for the week on the plate, `closed`, the tasks they work on that are done
621
695
  * or dropped and not yet put away. Left out by a console older than it.
622
696
  */
623
697
  export const restWorkSchema = z.object({
@@ -991,6 +1065,53 @@ export const restPackTokenInputSchema = z.object({
991
1065
  });
992
1066
  export type RestPackTokenInput = z.infer<typeof restPackTokenInputSchema>;
993
1067
 
1068
+ /**
1069
+ * `POST .../pack-token/connect` (ADR-0036): what the person is shown, as the deployment said it,
1070
+ * the code and the page to approve it on, until when, and how often the console asks again. The
1071
+ * secret the console claims the token with stays with the console.
1072
+ */
1073
+ export const restPackConnectStartedSchema = z.object({
1074
+ code: z.string(),
1075
+ url: z.string(),
1076
+ expiresAt: instant,
1077
+ intervalSeconds: z.int().check(z.positive()),
1078
+ });
1079
+ export type RestPackConnectStarted = z.infer<typeof restPackConnectStartedSchema>;
1080
+
1081
+ /**
1082
+ * `GET .../pack-token/connect`: how the connection stands. Nothing asked; waiting for the
1083
+ * person, with what they were shown; connected, the token kept and the project as it stands; or
1084
+ * ended short of a token, and why.
1085
+ */
1086
+ const packConnectNone = z.object({ status: z.literal("none") });
1087
+ const packConnectPending = z.object({
1088
+ status: z.literal("pending"),
1089
+ code: z.string(),
1090
+ url: z.string(),
1091
+ expiresAt: instant,
1092
+ intervalSeconds: z.int().check(z.positive()),
1093
+ });
1094
+ const packConnectEnded = z.object({
1095
+ status: z.literal("ended"),
1096
+ reason: z.enum(PACK_CONNECT_ENDINGS),
1097
+ });
1098
+ export const restPackConnectSchema = z.union([
1099
+ packConnectNone,
1100
+ packConnectPending,
1101
+ z.object({ status: z.literal("connected"), project: restProjectSchema }),
1102
+ packConnectEnded,
1103
+ ]);
1104
+ export type RestPackConnect = z.infer<typeof restPackConnectSchema>;
1105
+
1106
+ /** `GET /api/v1/workspace/pack-token/connect`: the same, for the workspace's token, with the workspace as it stands once connected. */
1107
+ export const restWorkspacePackConnectSchema = z.union([
1108
+ packConnectNone,
1109
+ packConnectPending,
1110
+ z.object({ status: z.literal("connected"), workspace: restWorkspaceSchema }),
1111
+ packConnectEnded,
1112
+ ]);
1113
+ export type RestWorkspacePackConnect = z.infer<typeof restWorkspacePackConnectSchema>;
1114
+
994
1115
  /** `GET .../skills/one?path=&cursor=`: a page of a skill's context, as the pack answers `load_skill` (`RestSkill` of `@skillcdn/core`); `read` is `null` unless `ready`. */
995
1116
  export const restSkillLoadSchema = z.object({
996
1117
  ...packEnvelope,
@@ -1177,6 +1177,7 @@ table {
1177
1177
  display: grid;
1178
1178
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
1179
1179
  gap: var(--sc-space-4);
1180
+ align-items: start;
1180
1181
  }
1181
1182
 
1182
1183
  .sc-input {
@@ -1237,6 +1238,150 @@ table {
1237
1238
  justify-self: start;
1238
1239
  }
1239
1240
 
1241
+ /* The workers of a task on its form: each a chip with the way to take them off, and the way to add one. */
1242
+ .sc-chips {
1243
+ display: flex;
1244
+ flex-wrap: wrap;
1245
+ align-items: center;
1246
+ gap: var(--sc-space-2);
1247
+ }
1248
+
1249
+ .sc-chip {
1250
+ display: inline-flex;
1251
+ align-items: center;
1252
+ gap: var(--sc-space-1);
1253
+ min-height: var(--sc-control-size);
1254
+ padding: 0 var(--sc-space-1) 0 var(--sc-space-2);
1255
+ border: var(--sc-border);
1256
+ border-radius: var(--sc-radius-md);
1257
+ background: var(--sc-color-bg-inset);
1258
+ font-size: var(--sc-text-sm);
1259
+ }
1260
+
1261
+ .sc-chip-remove,
1262
+ .sc-picker-clear {
1263
+ display: inline-grid;
1264
+ flex: none;
1265
+ place-items: center;
1266
+ width: 1.5rem;
1267
+ height: 1.5rem;
1268
+ border: 0;
1269
+ border-radius: var(--sc-radius-sm);
1270
+ background: transparent;
1271
+ color: var(--sc-color-text-muted);
1272
+ font-size: var(--sc-text-md);
1273
+ line-height: 1;
1274
+ cursor: pointer;
1275
+ }
1276
+
1277
+ .sc-chip-remove:hover:not(:disabled),
1278
+ .sc-picker-clear:hover:not(:disabled) {
1279
+ background: var(--sc-color-bg-raised);
1280
+ color: var(--sc-color-text);
1281
+ }
1282
+
1283
+ .sc-chip-remove:focus-visible,
1284
+ .sc-picker-clear:focus-visible {
1285
+ outline: var(--sc-focus-ring);
1286
+ }
1287
+
1288
+ .sc-chips .sc-chip-add {
1289
+ width: auto;
1290
+ min-width: 10rem;
1291
+ }
1292
+
1293
+ /* A picker: the field that searches a listing, its page of results under it, and the choice in its place. */
1294
+ .sc-picker {
1295
+ position: relative;
1296
+ min-width: 0;
1297
+ }
1298
+
1299
+ .sc-picker-chosen {
1300
+ display: flex;
1301
+ align-items: center;
1302
+ gap: var(--sc-space-2);
1303
+ min-height: var(--sc-control-size);
1304
+ padding: var(--sc-space-1) var(--sc-space-1) var(--sc-space-1) var(--sc-space-3);
1305
+ border: 1px solid var(--sc-color-border-strong);
1306
+ border-radius: var(--sc-radius-md);
1307
+ background: var(--sc-color-field);
1308
+ font-size: var(--sc-text-sm);
1309
+ }
1310
+
1311
+ .sc-picker-value {
1312
+ flex: 1;
1313
+ min-width: 0;
1314
+ overflow: hidden;
1315
+ text-overflow: ellipsis;
1316
+ white-space: nowrap;
1317
+ }
1318
+
1319
+ .sc-picker-list {
1320
+ position: absolute;
1321
+ z-index: 40;
1322
+ top: calc(100% + var(--sc-space-1));
1323
+ right: 0;
1324
+ left: 0;
1325
+ max-height: 18rem;
1326
+ margin: 0;
1327
+ padding: var(--sc-space-1);
1328
+ overflow-y: auto;
1329
+ list-style: none;
1330
+ border: var(--sc-border);
1331
+ border-radius: var(--sc-radius-md);
1332
+ background: var(--sc-color-bg-inset);
1333
+ box-shadow: var(--sc-shadow-stage);
1334
+ font-size: var(--sc-text-sm);
1335
+ }
1336
+
1337
+ /* Where the window has no room below the field, and more above, the list stands above it. */
1338
+ .sc-picker-list-above {
1339
+ top: auto;
1340
+ bottom: calc(100% + var(--sc-space-1));
1341
+ }
1342
+
1343
+ .sc-picker-option {
1344
+ padding: var(--sc-space-2);
1345
+ overflow: hidden;
1346
+ border-radius: var(--sc-radius-sm);
1347
+ text-overflow: ellipsis;
1348
+ white-space: nowrap;
1349
+ cursor: pointer;
1350
+ }
1351
+
1352
+ .sc-picker-option-active {
1353
+ background: var(--sc-color-bg-raised);
1354
+ }
1355
+
1356
+ .sc-picker-note {
1357
+ padding: var(--sc-space-2);
1358
+ }
1359
+
1360
+ /* The workers of a task on a card or a row: pictures side by side, and how many more. */
1361
+ .sc-workers {
1362
+ display: inline-flex;
1363
+ align-items: center;
1364
+ }
1365
+
1366
+ .sc-workers > * + * {
1367
+ margin-left: -0.375rem;
1368
+ }
1369
+
1370
+ .sc-workers-more {
1371
+ margin-inline: var(--sc-space-1);
1372
+ color: var(--sc-color-text-muted);
1373
+ font-size: var(--sc-text-xs);
1374
+ font-weight: var(--sc-weight-medium);
1375
+ }
1376
+
1377
+ .sc-worker-list {
1378
+ display: grid;
1379
+ gap: var(--sc-space-1);
1380
+ margin: 0;
1381
+ padding: 0;
1382
+ list-style: none;
1383
+ }
1384
+
1240
1385
  .sc-form-actions {
1241
1386
  display: flex;
1242
1387
  flex-wrap: wrap;
@@ -2905,8 +3050,10 @@ table {
2905
3050
  white-space: nowrap;
2906
3051
  }
2907
3052
 
2908
- /* Lit a little under the pointer by its colour, not by a filter, which would light the card within it too. */
2909
- .sc-calendar-bar:hover {
3053
+ /* The task under the pointer is lit a little by its colour, in every piece of its bar and in
3054
+ every week it crosses, never one piece alone; by its colour and not by a filter, which would
3055
+ light the card within it too. */
3056
+ .sc-calendar-bar-pointed {
2910
3057
  background: color-mix(in srgb, var(--sc-category-fill), white 12%);
2911
3058
  }
2912
3059
 
@@ -2924,6 +3071,18 @@ table {
2924
3071
  border-end-end-radius: var(--sc-radius-full);
2925
3072
  }
2926
3073
 
3074
+ /* The title of a bar with more days in the week runs along the bar as far as its days reach,
3075
+ less the room of its end, instead of stopping at the first day's edge: above every piece,
3076
+ the one under the pointer included, and under the cards; the pointer goes through it to the
3077
+ piece of the day beneath. The days are the first piece's own custom property. */
3078
+ .sc-calendar-task-title-spanning {
3079
+ position: relative;
3080
+ z-index: 25;
3081
+ flex: none;
3082
+ max-width: calc(var(--sc-span, 1) * 100% - 2.25rem);
3083
+ pointer-events: none;
3084
+ }
3085
+
2927
3086
  .sc-calendar-bar.sc-calendar-late {
2928
3087
  border-color: color-mix(in srgb, var(--sc-color-danger) 70%, transparent);
2929
3088
  color: var(--sc-color-danger);
@@ -3364,18 +3523,12 @@ table {
3364
3523
  position: relative;
3365
3524
  }
3366
3525
 
3367
- /* The item under the pointer rises above its neighbours, which would otherwise paint over its card. */
3368
- .sc-calendar-task:hover,
3369
- .sc-calendar-task:focus-visible,
3370
- .sc-calendar-bar:hover,
3371
- .sc-calendar-bar:focus-visible {
3372
- z-index: 20;
3373
- }
3374
-
3526
+ /* The card rises above its neighbours and above a title running along a bar; the item under the
3527
+ pointer itself stays in its layer, so that nothing of it covers such a title. */
3375
3528
  .sc-task-tip {
3376
3529
  position: absolute;
3377
3530
  left: 0;
3378
- z-index: 20;
3531
+ z-index: 30;
3379
3532
  width: max-content;
3380
3533
  max-width: 20rem;
3381
3534
  padding: var(--sc-space-2) var(--sc-space-3);
@@ -4130,3 +4283,158 @@ table {
4130
4283
  .sc-goal-view-list {
4131
4284
  display: block;
4132
4285
  }
4286
+
4287
+ /* The workspace's settings (ADR-0035): each setting a row, its name beside its value; on the form,
4288
+ its name and its hint beside a control of a bounded width, so that a short choice is not a
4289
+ field as wide as the page. */
4290
+ .sc-settings-list {
4291
+ display: grid;
4292
+ margin: 0;
4293
+ font-size: var(--sc-text-sm);
4294
+ }
4295
+
4296
+ .sc-settings-item {
4297
+ display: grid;
4298
+ grid-template-columns: minmax(8rem, 14rem) minmax(0, 1fr);
4299
+ gap: var(--sc-space-1) var(--sc-space-5);
4300
+ padding: var(--sc-space-3) 0;
4301
+ border-top: var(--sc-border);
4302
+ }
4303
+
4304
+ .sc-settings-item:first-child {
4305
+ padding-top: 0;
4306
+ border-top: 0;
4307
+ }
4308
+
4309
+ .sc-settings-item:last-child {
4310
+ padding-bottom: 0;
4311
+ }
4312
+
4313
+ .sc-settings-item dt {
4314
+ color: var(--sc-color-text-muted);
4315
+ }
4316
+
4317
+ .sc-settings-item dd {
4318
+ margin: 0;
4319
+ }
4320
+
4321
+ .sc-settings-note {
4322
+ margin-left: var(--sc-space-3);
4323
+ color: var(--sc-color-text-muted);
4324
+ }
4325
+
4326
+ .sc-settings-form {
4327
+ gap: 0;
4328
+ }
4329
+
4330
+ .sc-settings-row {
4331
+ display: grid;
4332
+ grid-template-columns: minmax(0, 1fr) minmax(14rem, 20rem);
4333
+ gap: var(--sc-space-3) var(--sc-space-6);
4334
+ align-items: start;
4335
+ padding: var(--sc-space-4) 0;
4336
+ border-top: var(--sc-border);
4337
+ }
4338
+
4339
+ .sc-settings-row:first-child {
4340
+ padding-top: 0;
4341
+ border-top: 0;
4342
+ }
4343
+
4344
+ .sc-settings-label {
4345
+ display: block;
4346
+ font-weight: var(--sc-weight-medium);
4347
+ }
4348
+
4349
+ .sc-settings-hint {
4350
+ max-width: var(--sc-width-prose);
4351
+ margin: var(--sc-space-1) 0 0;
4352
+ color: var(--sc-color-text-muted);
4353
+ font-size: var(--sc-text-sm);
4354
+ }
4355
+
4356
+ .sc-settings-control {
4357
+ display: grid;
4358
+ justify-items: start;
4359
+ gap: var(--sc-space-2);
4360
+ }
4361
+
4362
+ .sc-settings-control .sc-input {
4363
+ width: 100%;
4364
+ }
4365
+
4366
+ .sc-settings-form .sc-form-actions {
4367
+ padding-top: var(--sc-space-4);
4368
+ border-top: var(--sc-border);
4369
+ }
4370
+
4371
+ @media (max-width: 40rem) {
4372
+ .sc-settings-item,
4373
+ .sc-settings-row {
4374
+ grid-template-columns: minmax(0, 1fr);
4375
+ }
4376
+ }
4377
+
4378
+ /* The pack token (ADR-0028, ADR-0036): what it is for first, then the two ways to one side by
4379
+ side, each a card with its words before its controls, and the code shown large while the
4380
+ page waits for the approval. */
4381
+ .sc-pack-intro {
4382
+ max-width: var(--sc-width-prose);
4383
+ margin: 0 0 var(--sc-space-5);
4384
+ color: var(--sc-color-text-muted);
4385
+ font-size: var(--sc-text-sm);
4386
+ }
4387
+
4388
+ .sc-pack-ways {
4389
+ display: grid;
4390
+ grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
4391
+ gap: var(--sc-space-4);
4392
+ }
4393
+
4394
+ .sc-pack-way {
4395
+ display: grid;
4396
+ align-content: start;
4397
+ gap: var(--sc-space-3);
4398
+ padding: var(--sc-space-4);
4399
+ border: var(--sc-border);
4400
+ border-radius: var(--sc-radius-lg);
4401
+ background: var(--sc-color-sheet);
4402
+ }
4403
+
4404
+ .sc-pack-way-title {
4405
+ font-size: var(--sc-text-md);
4406
+ font-weight: var(--sc-weight-medium);
4407
+ }
4408
+
4409
+ .sc-pack-way .sc-field-hint {
4410
+ margin-top: 0;
4411
+ }
4412
+
4413
+ .sc-pack-connect {
4414
+ display: grid;
4415
+ gap: var(--sc-space-3);
4416
+ }
4417
+
4418
+ .sc-pack-connect-code {
4419
+ display: flex;
4420
+ flex-wrap: wrap;
4421
+ align-items: baseline;
4422
+ gap: var(--sc-space-3);
4423
+ }
4424
+
4425
+ .sc-pack-code {
4426
+ font-size: var(--sc-text-xl);
4427
+ letter-spacing: 0.12em;
4428
+ }
4429
+
4430
+ .sc-pack-connect-waiting {
4431
+ display: flex;
4432
+ align-items: center;
4433
+ gap: var(--sc-space-2);
4434
+ color: var(--sc-color-text-muted);
4435
+ font-size: var(--sc-text-sm);
4436
+ }
4437
+
4438
+ .sc-pack-actions {
4439
+ margin-top: var(--sc-space-5);
4440
+ }
package/src/vocabulary.ts CHANGED
@@ -30,6 +30,9 @@ export const GOAL_STATES = ["planned", "active", "done", "dropped"] as const;
30
30
 
31
31
  export type GoalState = (typeof GOAL_STATES)[number];
32
32
 
33
+ /** The states a goal is still open in: planned, or being worked towards. */
34
+ export const OPEN_GOAL_STATES: readonly GoalState[] = ["planned", "active"];
35
+
33
36
  /**
34
37
  * The colours a category, or a project's mark, may be shown in: names, so that a custom console
35
38
  * paints them in its own tokens and no value a person typed reaches a stylesheet.
@@ -97,6 +100,30 @@ export const PROJECT_VISIBILITIES = ["workspace", "private"] as const;
97
100
 
98
101
  export type ProjectVisibility = (typeof PROJECT_VISIBILITIES)[number];
99
102
 
103
+ /**
104
+ * Who sees whom on the People page and in the pickers (ADR-0035): everyone of the workspace,
105
+ * or only the people one shares a project with; an administrator always sees everyone.
106
+ */
107
+ export const PEOPLE_VISIBILITIES = ["everyone", "shared_projects"] as const;
108
+
109
+ export type PeopleVisibility = (typeof PEOPLE_VISIBILITIES)[number];
110
+
111
+ /**
112
+ * The day the calendars begin a week on (ADR-0035): as the language of the pages has it, or
113
+ * Monday, or Sunday, for the whole workspace.
114
+ */
115
+ export const WEEK_STARTS = ["auto", "monday", "sunday"] as const;
116
+
117
+ export type WeekStart = (typeof WEEK_STARTS)[number];
118
+
119
+ /**
120
+ * How a private pack's connection ended short of a token (ADR-0036): the code expired before
121
+ * anyone approved, the person declined at the deployment, or the deployment refused the request.
122
+ */
123
+ export const PACK_CONNECT_ENDINGS = ["expired", "declined", "refused"] as const;
124
+
125
+ export type PackConnectEnding = (typeof PACK_CONNECT_ENDINGS)[number];
126
+
100
127
  /** What a run is doing: at work, waiting for a person's decision, or over in one of three ways. */
101
128
  export const RUN_STATUSES = ["running", "waiting", "finished", "failed", "abandoned"] as const;
102
129
 
@@ -153,6 +180,7 @@ export const EVENT_KINDS = [
153
180
  "person.unlisted",
154
181
  "person.deactivated",
155
182
  "person.reactivated",
183
+ "workspace.updated",
156
184
  "category.created",
157
185
  "category.updated",
158
186
  "category.removed",