jackherd 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/INSTALL.md +2 -0
- package/{NOTICE → NOTICE.txt} +1 -1
- package/README.md +94 -1033
- package/assets/icons/bark-dark.svg +1 -1
- package/assets/icons/bark.svg +1 -1
- package/assets/icons/bowl-dark.svg +1 -1
- package/assets/icons/bowl.svg +1 -1
- package/assets/icons/decide-dark.svg +1 -1
- package/assets/icons/decide.svg +1 -1
- package/assets/icons/ear-dark.svg +1 -1
- package/assets/icons/ear.svg +1 -1
- package/assets/icons/lanes-dark.svg +1 -1
- package/assets/icons/lanes.svg +1 -1
- package/assets/icons/plan-dark.svg +1 -1
- package/assets/icons/plan.svg +1 -1
- package/dist/{chunk-OBUWTQ6Z.js → chunk-M6DDZEW4.js} +24 -10
- package/dist/config.d.ts +11 -5
- package/dist/config.js +53 -21
- package/dist/plan-format.d.ts +73 -7
- package/dist/plan-format.js +180 -100
- package/docs/workflow-dark.svg +1 -1
- package/docs/workflow.svg +1 -1
- package/herdr-plugin/herdr-plugin.toml +1 -1
- package/install.sh +1 -1
- package/kit/hooks/hooks/approval-gate/herdr-panes.ts +2 -12
- package/kit/hooks/hooks/approval-gate/index.ts +10 -8
- package/kit/hooks/hooks/approval-gate/run.ts +21 -12
- package/kit/hooks/hooks/ask-hint/decision.ts +3 -3
- package/kit/hooks/hooks/git-guard/decision.ts +189 -15
- package/kit/hooks/hooks/git-guard/index.ts +13 -3
- package/kit/hooks/hooks/git-guard/push-target.ts +85 -0
- package/kit/hooks/hooks/git-guard/repository.ts +63 -0
- package/kit/hooks/hooks/merge-guard/check-free.ts +1 -13
- package/kit/hooks/hooks/merge-guard/decision.ts +342 -102
- package/kit/hooks/hooks/merge-guard/index.ts +6 -4
- package/kit/hooks/hooks/merge-guard/plain-path.ts +15 -0
- package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +23 -0
- package/kit/hooks/hooks/merge-guard/plan-approval-settings.ts +142 -0
- package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +196 -0
- package/kit/hooks/hooks/merge-guard/plan-approval.ts +168 -0
- package/kit/hooks/hooks/merge-guard/plan-growth.ts +40 -0
- package/kit/hooks/hooks/merge-guard/plan-pointers.ts +194 -0
- package/kit/hooks/hooks/merge-guard/pull-request.ts +67 -21
- package/kit/hooks/hooks/merge-guard/run.ts +118 -29
- package/kit/hooks/hooks/merge-guard/settings.ts +8 -8
- package/kit/hooks/hooks/merge-guard/worker.ts +3 -3
- package/kit/hooks/hooks/shared/base-branch.ts +23 -0
- package/kit/hooks/hooks/shared/board-lanes.ts +93 -0
- package/kit/hooks/hooks/shared/command-text-runners.ts +71 -0
- package/kit/hooks/hooks/shared/command.ts +14 -2
- package/kit/hooks/hooks/shared/commit.ts +2 -0
- package/kit/hooks/hooks/shared/dialog-answers.ts +4 -1
- package/kit/hooks/hooks/shared/json.ts +10 -0
- package/kit/hooks/hooks/shared/lane.ts +57 -0
- package/kit/hooks/hooks/shared/once.ts +8 -0
- package/kit/hooks/hooks/shared/own-pane.ts +31 -0
- package/kit/hooks/hooks/shared/pane-agent.ts +24 -0
- package/kit/hooks/hooks/shared/plan-open-phases.ts +114 -0
- package/kit/hooks/hooks/shared/program-position.ts +148 -5
- package/kit/hooks/hooks/shared/shell-aliases.ts +258 -0
- package/kit/hooks/hooks/shared/shell-snapshots.ts +68 -0
- package/kit/hooks/hooks/shared/shell.ts +167 -20
- package/kit/hooks/hooks.json +11 -2
- package/kit/hooks/statusline.sh +29 -16
- package/kit/review/skills/diagnose/SKILL.md +1 -1
- package/kit/review/skills/review/SKILL.md +2 -0
- package/kit/rules/rules/kit/testing.md +2 -5
- package/kit/rules/rules/kit/writing-for-agents.md +3 -1
- package/kit/workflow/agents/spec-review.md +1 -1
- package/kit/workflow/skills/follow/SKILL.md +3 -3
- package/kit/workflow/skills/grill/SKILL.md +1 -1
- package/kit/workflow/skills/implement/SKILL.md +10 -11
- package/kit/workflow/skills/orchestrate/SKILL.md +155 -117
- package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +17 -0
- package/kit/workflow/skills/orchestrate/references/herd.md +27 -0
- package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -0
- package/kit/workflow/skills/orchestrate/references/sensitive-input.md +23 -0
- package/kit/workflow/skills/plan/SKILL.md +5 -2
- package/kit/workflow/skills/plan/references/template-English.md +6 -0
- package/kit/workflow/skills/plan/references/template-German.md +6 -0
- package/package.json +3 -2
- package/recipes/README.md +21 -0
- package/recipes/github-issues.md +23 -0
- package/recipes/github-projects.md +22 -0
- package/recipes/jira-cli.md +26 -0
- package/recipes/jira-rest.md +24 -0
- package/recipes/linear.md +25 -0
- package/recipes/sentry.md +18 -0
- package/recipes/sources/jira.ts +51 -0
- package/recipes/sources/linear.ts +19 -0
- package/recipes/sources/sentry.ts +12 -0
- package/recipes/sources.md +80 -0
- package/recipes/status-page.md +19 -0
- package/schemas/jackherd.schema.json +42 -18
- package/src/check/command.ts +2 -2
- package/src/check/parallel-report.ts +19 -9
- package/src/cli.ts +1 -0
- package/src/config/context-shares.ts +130 -0
- package/src/config/read-config.ts +12 -4
- package/src/config/schema.ts +75 -20
- package/src/context/auto-compact.ts +68 -0
- package/src/context/command.ts +68 -17
- package/src/context/coordinator-name.ts +27 -9
- package/src/context/index.ts +0 -1
- package/src/context/level.ts +18 -11
- package/src/context/report.ts +6 -3
- package/src/plan-format/dependencies.ts +144 -32
- package/src/plan-format/index.ts +6 -0
- package/src/plan-format/lanes.ts +6 -0
- package/src/plan-format/progress.ts +19 -8
- package/src/plans/command.ts +14 -5
- package/src/plans/current-phase.ts +63 -7
- package/src/plans/current-task.ts +51 -0
- package/src/plans/ensure-panes.ts +25 -8
- package/src/plans/frame.ts +99 -0
- package/src/plans/git.ts +8 -0
- package/src/plans/herd-memory.ts +107 -0
- package/src/plans/lane-plans.ts +5 -3
- package/src/plans/lane-summary.ts +222 -0
- package/src/plans/layout-phase.ts +148 -0
- package/src/plans/layout-summary.ts +140 -0
- package/src/plans/layout.ts +164 -420
- package/src/plans/live.ts +11 -6
- package/src/plans/plan-panes.ts +15 -0
- package/src/plans/planned.ts +9 -42
- package/src/plans/selection.ts +2 -6
- package/src/plans/settings.ts +2 -7
- package/src/plans/snapshot.ts +38 -39
- package/src/plans/symbols.ts +2 -2
- package/src/plans/texts.ts +32 -31
- package/src/plans/view.ts +0 -1
- package/src/plans/worked-phases.ts +2 -2
- package/src/settled/board-row.ts +133 -0
- package/src/settled/check.ts +129 -0
- package/src/settled/command.ts +65 -0
- package/src/settled/evidence-version.ts +14 -0
- package/src/settled/options.ts +69 -0
- package/src/settled/send.ts +27 -0
- package/src/settled/settle.ts +87 -0
- package/src/settled/topics.ts +56 -0
- package/src/sync/render.ts +13 -4
- package/src/sync/sync.ts +25 -5
- package/src/terminal/herdr.ts +46 -10
- package/src/terminal/none.ts +1 -0
- package/src/terminal/types.ts +15 -2
- package/src/vocabulary/index.ts +47 -11
- package/src/watch/appointments.ts +205 -0
- package/src/watch/command.ts +62 -19
- package/src/watch/lane-report.ts +11 -3
- package/src/watch/texts.ts +35 -59
- package/src/watch/watch-events.ts +21 -28
- package/kit/hooks/hooks/approval-gate/lane.ts +0 -14
package/dist/plan-format.d.ts
CHANGED
|
@@ -22,9 +22,19 @@ declare const ADDRESS_FORM: Record<PlanLanguage, Record<ResponseAddress, string>
|
|
|
22
22
|
* a config value.
|
|
23
23
|
*/
|
|
24
24
|
declare const SELF_MERGE_LABEL: Record<PlanLanguage, string>;
|
|
25
|
+
/**
|
|
26
|
+
* The options of the question that approves a plan for all its lanes (``Plan `<path>` ``) and of the one that revokes
|
|
27
|
+
* that approval. The merge guard hook lifts its question for a plain merge of such a lane only when the newest answer
|
|
28
|
+
* to a question naming the plan is exactly `approve`, so the labels are fixed per language and not a config value.
|
|
29
|
+
*/
|
|
30
|
+
declare const PLAN_APPROVAL_LABELS: Record<PlanLanguage, {
|
|
31
|
+
approve: string;
|
|
32
|
+
revoke: string;
|
|
33
|
+
}>;
|
|
25
34
|
/**
|
|
26
35
|
* The words of an approval: the two option labels of the coordinator's question about a lane's call, which the
|
|
27
|
-
* approval gate hook matches exactly, and what the hook tells the lane when it
|
|
36
|
+
* approval gate hook matches exactly and which follow the response language, and what the hook tells the lane when it
|
|
37
|
+
* denies, which is English. Fixed per language, not config.
|
|
28
38
|
*/
|
|
29
39
|
declare const APPROVAL_TEXTS: Record<PlanLanguage, {
|
|
30
40
|
allow: string;
|
|
@@ -32,6 +42,21 @@ declare const APPROVAL_TEXTS: Record<PlanLanguage, {
|
|
|
32
42
|
pending: string;
|
|
33
43
|
denied: string;
|
|
34
44
|
}>;
|
|
45
|
+
/**
|
|
46
|
+
* The board section of agreed times, which the watcher reads: its heading, the header names of its columns (when,
|
|
47
|
+
* action, origin, status; found by name, in any order) and the lower-case words a status cell of a still open
|
|
48
|
+
* appointment starts with.
|
|
49
|
+
*/
|
|
50
|
+
declare const APPOINTMENT_VOCABULARY: Record<PlanLanguage, {
|
|
51
|
+
heading: string;
|
|
52
|
+
columns: {
|
|
53
|
+
when: string;
|
|
54
|
+
action: string;
|
|
55
|
+
origin: string;
|
|
56
|
+
status: string;
|
|
57
|
+
};
|
|
58
|
+
open: readonly string[];
|
|
59
|
+
}>;
|
|
35
60
|
declare const isPlanLanguage: (value: string) => value is PlanLanguage;
|
|
36
61
|
declare const PLAN_STATUS: {
|
|
37
62
|
readonly done: "done";
|
|
@@ -76,6 +101,8 @@ type PlanVocabulary = {
|
|
|
76
101
|
noReviewGate: string;
|
|
77
102
|
/** Marks a lane as closed when its name cell contains it. */
|
|
78
103
|
laneClosed: string;
|
|
104
|
+
/** Opens the line a lane's hand-over file must hold as `<word>: 0`, so the coordinator hands over only after a wave. */
|
|
105
|
+
openSubagents: string;
|
|
79
106
|
/** Words a starts-after cell begins with when the lane may start at once. */
|
|
80
107
|
startsNow: readonly string[];
|
|
81
108
|
/** Opens the parenthesis by which a board row's phases cell records a user's side order in prose. */
|
|
@@ -190,7 +217,8 @@ declare const parsePlanPhases: (markdown: string, language: PlanLanguage) => Pla
|
|
|
190
217
|
* A row of the dependency table. `after` holds the phases it waits for, `external` the entries that name no phase
|
|
191
218
|
* (`User: Zuruf`) or a range that cannot be expanded; `writes` are the paths of the writes cell (`''` is the whole
|
|
192
219
|
* repository), `writesLike` the phases whose sets the cell takes over (`wie 15 und 16`); `serialMarkers` are the
|
|
193
|
-
* entries of the marker cell that keep two phases apart
|
|
220
|
+
* entries of the marker cell that keep two phases apart, `releases` the packages its release markers name
|
|
221
|
+
* (`Release jackherd 1.8.0` is `jackherd`).
|
|
194
222
|
*/
|
|
195
223
|
type PhaseDependency = {
|
|
196
224
|
id: string;
|
|
@@ -199,6 +227,13 @@ type PhaseDependency = {
|
|
|
199
227
|
writes: string[];
|
|
200
228
|
writesLike: string[];
|
|
201
229
|
serialMarkers: string[];
|
|
230
|
+
releases: string[];
|
|
231
|
+
};
|
|
232
|
+
/** The open phases in waves (each starts once the ones before it are done), the longest chain, and the open phases no wave holds. */
|
|
233
|
+
type PhaseSchedule = {
|
|
234
|
+
waves: string[][];
|
|
235
|
+
criticalPath: string[];
|
|
236
|
+
unplaced: string[];
|
|
202
237
|
};
|
|
203
238
|
/** A phase heading (1-based line) the dependency table has no row for. */
|
|
204
239
|
type PhaseWithoutRow = PhaseHeading;
|
|
@@ -215,15 +250,32 @@ declare const phasesWithoutDependencyRow: (markdown: string, language: PlanLangu
|
|
|
215
250
|
* every phase in its `Depends on` cell is, and the cell names no external gate: nothing in the plan says such a gate
|
|
216
251
|
* holds, so the phase stays out and the caller asks. Two phases share a group only when no path of one lies at or
|
|
217
252
|
* below a path of the other (a root path lies below nothing and above everything) and they share no serial marker,
|
|
218
|
-
* compared without case
|
|
219
|
-
*
|
|
220
|
-
*
|
|
253
|
+
* compared without case, and release no package the other releases: a release marker naming it (`Release jackherd
|
|
254
|
+
* 1.8.0`) or a write of its release files (`CHANGELOG.md`, `package.json` in its directory), the package compared by
|
|
255
|
+
* its name without scope and the last segment of its directory. A release marker without a name keeps nobody apart.
|
|
256
|
+
* A `wie N` writes cell takes the set of phase N; a phase whose set is empty, whose `wie` target is unknown, or whose
|
|
257
|
+
* `wie` chain is a cycle, cannot be shown disjoint and stays out. Whether two phases collide in content is not decided here.
|
|
221
258
|
*/
|
|
222
259
|
declare const parallelGroups: (rows: readonly PhaseDependency[], doneIds: ReadonlySet<string>) => string[][];
|
|
260
|
+
/**
|
|
261
|
+
* The whole sequence of parallel groups, computed ahead: the first wave is the first group of `parallelGroups`, each
|
|
262
|
+
* later wave the first group once the waves before it are done. A phase `parallelGroups` never offers (an external
|
|
263
|
+
* gate, an empty or unresolvable writes set) is in no wave, and neither is a phase waiting for it.
|
|
264
|
+
*/
|
|
265
|
+
declare const phaseWaves: (rows: readonly PhaseDependency[], doneIds: ReadonlySet<string>) => string[][];
|
|
266
|
+
/**
|
|
267
|
+
* The longest chain of open phases in `waves`, counted in phases: a phase follows another that it depends on, or one
|
|
268
|
+
* of an earlier wave it shares a serial marker or a package release with. A shared path alone does not chain, so a
|
|
269
|
+
* plan with more waves than its critical path is long loses the difference to overlapping writes. The first longest
|
|
270
|
+
* chain in wave order; empty without waves.
|
|
271
|
+
*/
|
|
272
|
+
declare const criticalPath: (rows: readonly PhaseDependency[], waves: readonly string[][]) => string[];
|
|
223
273
|
/** The phases as a report names them, side by side: `P19 ∥ P20`. */
|
|
224
274
|
declare const phasesText: (phases: readonly string[]) => string;
|
|
225
|
-
/** `parallelGroups` of the plan's own dependency table and ticks (a phase is done
|
|
275
|
+
/** `parallelGroups` of the plan's own dependency table and ticks (a phase is done once `isPhaseFinished`); null for a plan without a table. */
|
|
226
276
|
declare const parallelGroupsOfPlan: (markdown: string, language: PlanLanguage) => string[][] | null;
|
|
277
|
+
/** The waves, the critical path and the open phases in no wave, of the plan's own table and ticks; null for a plan without a table. */
|
|
278
|
+
declare const wavesOfPlan: (markdown: string, language: PlanLanguage) => PhaseSchedule | null;
|
|
227
279
|
|
|
228
280
|
/** The review-gate box `Dokumentation aktualisiert …` (`Documentation updated …`): a gate item starting with the documentation word. */
|
|
229
281
|
declare const isDocumentationGateItem: ({ gate, text }: ChecklistItem) => boolean;
|
|
@@ -282,6 +334,10 @@ type OpenLaneRow = {
|
|
|
282
334
|
};
|
|
283
335
|
/** Sprint tables name a phase `P4`; the sidebar title prints it the same way. */
|
|
284
336
|
declare const PHASE_ID_PREFIX = "P";
|
|
337
|
+
/** A phase as plans and reports name it: `P4`. */
|
|
338
|
+
declare const phaseLabel: (id: string) => string;
|
|
339
|
+
/** Phases as plans and reports name them, joined by `separator`: `P3 P4`, `P19 ∥ P20`. */
|
|
340
|
+
declare const phaseLabels: (ids: readonly string[], separator: string) => string;
|
|
285
341
|
/** The phase ids a phases cell of the lanes table names, read as `parseSprintLanes` reads them. */
|
|
286
342
|
declare const phaseIdsOfCell: (cell: string, language: PlanLanguage) => string[];
|
|
287
343
|
declare const laneNameOf: (cell: string, previous: string | null) => string | null;
|
|
@@ -396,6 +452,16 @@ declare const sumPhases: (phases: readonly PhaseProgress[]) => {
|
|
|
396
452
|
done: number;
|
|
397
453
|
total: number;
|
|
398
454
|
};
|
|
455
|
+
/** The progress of the phase `id` of a plan; a phase the plan lacks has a null title and no checkboxes. */
|
|
456
|
+
declare const phaseProgress: (phases: readonly PlanPhase[], id: string) => PhaseProgress;
|
|
457
|
+
/**
|
|
458
|
+
* A phase is finished once every checkbox it has is ticked, its tasks and its review gate alike: `/implement` and
|
|
459
|
+
* `jh check --parallel` read it so, and an open documentation box keeps the phase open. A phase without a checkbox is
|
|
460
|
+
* never finished.
|
|
461
|
+
*/
|
|
462
|
+
declare const isPhaseFinished: ({ items }: Pick<PlanPhase, "items">) => boolean;
|
|
463
|
+
/** A phase with a checkbox that is not finished; a phase without a checkbox is neither. */
|
|
464
|
+
declare const isPhaseOpen: (phase: Pick<PlanPhase, "items">) => boolean;
|
|
399
465
|
/**
|
|
400
466
|
* A phase the lane ticked to the end in its worktree while the base branch does not carry those ticks yet: still
|
|
401
467
|
* the lane's work until it is merged, not a finished phase.
|
|
@@ -444,4 +510,4 @@ declare const readSprintDocument: (markdown: string, language: PlanLanguage) =>
|
|
|
444
510
|
*/
|
|
445
511
|
declare const readPhaseDependencies: (planMarkdown: string, language: PlanLanguage) => Record<string, string>;
|
|
446
512
|
|
|
447
|
-
export { ADDRESS_FORM, APPROVAL_TEXTS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, LANGUAGE_NAME, type LaneProgress, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, PHASE_CELL_LIMIT, PHASE_ID_PREFIX, PLAN_LANGUAGE, PLAN_STATUS, PLAN_VOCABULARY, PROSE_REASON, type PhaseDependency, type PhaseHeading, type PhaseProgress, type PhaseTally, type PhaseWithoutGate, type PhaseWithoutRow, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RESPONSE_ADDRESS, type ResponseAddress, type RowWithProse, type RowWithoutPhase, SELF_MERGE_LABEL, type SprintDocument, type SprintLane, type SprintPlanRow, type SprintPlanTable, type StatusWord, type StepProgress, activeItem, boardRowsWithProse, boardRowsWithoutPhase, classifyStatus, columnModels, countDecisions, currentPhase, currentPhaseOf, deferralTarget, differsFromColumn, endsPhase, findDrift, hasLanesTable, isCheckboxLine, isDocumentationGateItem, isOpenCheckboxLine, isPlanLanguage, isReviewGateLine, isTickedUnmerged, laneNameOf, laneProgress, mergedPhasesWithOpenBoxes, misplacedDeferrals, newPhasesWithoutReviewGate, onlyDocumentationOpen, parallelGroups, parallelGroupsOfPlan, parseDependencyTable, parsePlanPhases, parseSprintLanes, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdsOfCell, phaseIdsOfPlan, phasesText, phasesWithoutDependencyRow, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, shortTitle, sprintTitleOf, sumPhases, tallyPhases, withoutRepeats };
|
|
513
|
+
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, LANGUAGE_NAME, type LaneProgress, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, PHASE_CELL_LIMIT, PHASE_ID_PREFIX, PLAN_APPROVAL_LABELS, PLAN_LANGUAGE, PLAN_STATUS, PLAN_VOCABULARY, PROSE_REASON, type PhaseDependency, type PhaseHeading, type PhaseProgress, type PhaseSchedule, type PhaseTally, type PhaseWithoutGate, type PhaseWithoutRow, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RESPONSE_ADDRESS, type ResponseAddress, type RowWithProse, type RowWithoutPhase, SELF_MERGE_LABEL, type SprintDocument, type SprintLane, type SprintPlanRow, type SprintPlanTable, type StatusWord, type StepProgress, activeItem, boardRowsWithProse, boardRowsWithoutPhase, classifyStatus, columnModels, countDecisions, criticalPath, currentPhase, currentPhaseOf, deferralTarget, differsFromColumn, endsPhase, findDrift, hasLanesTable, isCheckboxLine, isDocumentationGateItem, isOpenCheckboxLine, isPhaseFinished, isPhaseOpen, isPlanLanguage, isReviewGateLine, isTickedUnmerged, laneNameOf, laneProgress, mergedPhasesWithOpenBoxes, misplacedDeferrals, newPhasesWithoutReviewGate, onlyDocumentationOpen, parallelGroups, parallelGroupsOfPlan, parseDependencyTable, parsePlanPhases, parseSprintLanes, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdsOfCell, phaseIdsOfPlan, phaseLabel, phaseLabels, phaseProgress, phaseWaves, phasesText, phasesWithoutDependencyRow, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, shortTitle, sprintTitleOf, sumPhases, tallyPhases, wavesOfPlan, withoutRepeats };
|
package/dist/plan-format.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ADDRESS_FORM,
|
|
3
|
+
APPOINTMENT_VOCABULARY,
|
|
3
4
|
APPROVAL_TEXTS,
|
|
4
5
|
LANGUAGE_NAME,
|
|
6
|
+
PLAN_APPROVAL_LABELS,
|
|
5
7
|
PLAN_LANGUAGE,
|
|
6
8
|
PLAN_STATUS,
|
|
7
9
|
PLAN_VOCABULARY,
|
|
@@ -11,7 +13,7 @@ import {
|
|
|
11
13
|
isPlanLanguage,
|
|
12
14
|
phaseHeading,
|
|
13
15
|
sectionHeading
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-M6DDZEW4.js";
|
|
15
17
|
|
|
16
18
|
// src/plan-format/lanes.ts
|
|
17
19
|
import { posix } from "path";
|
|
@@ -97,6 +99,8 @@ var LANE_NAME = /`([^`]+)`/;
|
|
|
97
99
|
var CONTINUATION = "\u21B3";
|
|
98
100
|
var PLAN_LINK = /\[([^\]]+)\]\(([^)]+)\)/;
|
|
99
101
|
var PHASE_ID_PREFIX = "P";
|
|
102
|
+
var phaseLabel = (id) => `${PHASE_ID_PREFIX}${id}`;
|
|
103
|
+
var phaseLabels = (ids, separator) => ids.map(phaseLabel).join(separator);
|
|
100
104
|
var PHASES_COLUMN = 3;
|
|
101
105
|
var CLAUSE_SEPARATOR = /([;,]|\.(?=\s))\s*/;
|
|
102
106
|
var ENTRY_END = /* @__PURE__ */ new Set([";", "."]);
|
|
@@ -348,6 +352,94 @@ var parsePlanPhases = (markdown, language) => {
|
|
|
348
352
|
return withArrivedItems(withPointedSections(phases, pointers, markdown));
|
|
349
353
|
};
|
|
350
354
|
|
|
355
|
+
// src/plan-format/progress.ts
|
|
356
|
+
var sumPhases = (phases) => ({
|
|
357
|
+
done: phases.reduce((sum, phase) => sum + phase.done, 0),
|
|
358
|
+
total: phases.reduce((sum, phase) => sum + phase.total, 0)
|
|
359
|
+
});
|
|
360
|
+
var phaseById = (phases, id) => phases.find((phase) => phase.id === id);
|
|
361
|
+
var phaseProgress = (phases, id) => {
|
|
362
|
+
const phase = phaseById(phases, id);
|
|
363
|
+
const items = phase?.items ?? [];
|
|
364
|
+
return {
|
|
365
|
+
id,
|
|
366
|
+
title: phase?.title ?? null,
|
|
367
|
+
done: items.filter((item) => item.done).length,
|
|
368
|
+
total: items.length,
|
|
369
|
+
items,
|
|
370
|
+
...phase?.deferred ? { deferred: phase.deferred } : {},
|
|
371
|
+
...phase?.touched ? { touched: true } : {}
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
var isPhaseFinished = ({ items }) => items.length > 0 && items.every(({ done }) => done);
|
|
375
|
+
var isPhaseOpen = (phase) => phase.items.length > 0 && !isPhaseFinished(phase);
|
|
376
|
+
var isWritten = ({ total }) => total > 0;
|
|
377
|
+
var newestWrittenStepIndex = (steps) => steps.reduce((last, { phases }, index) => phases.some(isWritten) ? index : last, -1);
|
|
378
|
+
var newestRowIndex = (steps) => steps.reduce((last, { step }, index) => step.withoutRow ? last : index, -1);
|
|
379
|
+
var isTouched = (phase) => phase.touched === true && isWritten(phase);
|
|
380
|
+
var isTickedUnmerged = (phase) => isTouched(phase) && isPhaseFinished(phase);
|
|
381
|
+
var newestStepWith = (steps, pick) => {
|
|
382
|
+
for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex--) {
|
|
383
|
+
const phase = pick(steps[stepIndex].phases);
|
|
384
|
+
if (phase) return { stepIndex, phaseId: phase.id };
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
};
|
|
388
|
+
var earlierPhase = (steps, before, id) => {
|
|
389
|
+
const planHref = steps[before].step.planHref;
|
|
390
|
+
for (let stepIndex = before - 1; stepIndex >= 0; stepIndex--) {
|
|
391
|
+
if (steps[stepIndex].step.planHref !== planHref) continue;
|
|
392
|
+
const phase = phaseById(steps[stepIndex].phases, id);
|
|
393
|
+
if (phase) return { phase, current: { stepIndex, phaseId: phase.id } };
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
};
|
|
397
|
+
var rowPhases = (steps, stepIndex) => {
|
|
398
|
+
const row = steps[stepIndex];
|
|
399
|
+
if (!row) return [];
|
|
400
|
+
const own = row.phases.map((phase) => ({ phase, current: { stepIndex, phaseId: phase.id } }));
|
|
401
|
+
const named = row.step.namedPhaseIds;
|
|
402
|
+
if (!named) return own;
|
|
403
|
+
return named.flatMap((id) => {
|
|
404
|
+
const located = own.find((entry) => entry.phase.id === id) ?? earlierPhase(steps, stepIndex, id);
|
|
405
|
+
return located ? [located] : [];
|
|
406
|
+
});
|
|
407
|
+
};
|
|
408
|
+
var touchedOpenPhaseOf = (phases) => phases.find((phase) => isTouched(phase) && isPhaseOpen(phase));
|
|
409
|
+
var lastTickedUnmergedOf = (phases) => phases.filter(isTickedUnmerged).at(-1);
|
|
410
|
+
var newestRowPhase = (steps) => {
|
|
411
|
+
const located = rowPhases(steps, newestRowIndex(steps)).filter(({ phase: phase2 }) => isWritten(phase2));
|
|
412
|
+
const phases = located.map(({ phase: phase2 }) => phase2);
|
|
413
|
+
const phase = touchedOpenPhaseOf(phases) ?? lastTickedUnmergedOf(phases) ?? phases.find(isPhaseOpen);
|
|
414
|
+
return located.find((entry) => entry.phase === phase)?.current ?? null;
|
|
415
|
+
};
|
|
416
|
+
var phaseFromTicks = (steps) => {
|
|
417
|
+
const touched = newestStepWith(steps, touchedOpenPhaseOf) ?? newestStepWith(steps, lastTickedUnmergedOf);
|
|
418
|
+
if (touched) return touched;
|
|
419
|
+
const lastIndex = newestWrittenStepIndex(steps);
|
|
420
|
+
const phase = steps[lastIndex]?.phases.find(isPhaseOpen);
|
|
421
|
+
return phase ? { stepIndex: lastIndex, phaseId: phase.id } : null;
|
|
422
|
+
};
|
|
423
|
+
var currentPhaseOf = (steps) => newestRowPhase(steps) ?? phaseFromTicks(steps);
|
|
424
|
+
var laneProgress = (lane, plans) => {
|
|
425
|
+
const steps = lane.steps.map((step) => {
|
|
426
|
+
const phases = step.planHref ? plans[step.planHref] : void 0;
|
|
427
|
+
return { step, phases: phases ? step.phaseIds.map((id) => phaseProgress(phases, id)) : [] };
|
|
428
|
+
});
|
|
429
|
+
return { lane, steps, current: currentPhaseOf(steps), ...sumPhases(steps.flatMap((step) => step.phases)) };
|
|
430
|
+
};
|
|
431
|
+
var currentPhase = (progress) => {
|
|
432
|
+
if (!progress.current) return null;
|
|
433
|
+
const { stepIndex, phaseId } = progress.current;
|
|
434
|
+
return phaseById(progress.steps[stepIndex].phases, phaseId) ?? null;
|
|
435
|
+
};
|
|
436
|
+
var activeItem = (phase) => phase.items.find((item) => !item.done) ?? null;
|
|
437
|
+
var shortTitle = (progress) => {
|
|
438
|
+
const phase = currentPhase(progress);
|
|
439
|
+
if (phase) return `${phaseLabel(phase.id)} \xB7 ${phase.done}/${phase.total}`;
|
|
440
|
+
return progress.total > 0 ? `\u2713 ${progress.done}/${progress.total}` : null;
|
|
441
|
+
};
|
|
442
|
+
|
|
351
443
|
// src/plan-format/dependencies.ts
|
|
352
444
|
var ID = "(\\d+[a-z]?)";
|
|
353
445
|
var PHASE_ID = new RegExp(`^\\s*${PHASE_ID_PREFIX}?${ID}\\b`, "i");
|
|
@@ -356,6 +448,9 @@ var BACKTICKED = /`([^`]+)`/g;
|
|
|
356
448
|
var NO_VALUE = /^[—–-]?$/;
|
|
357
449
|
var TRAILING_NOTE = /\s*\([^)]*\)\s*$/;
|
|
358
450
|
var GLOB_CHARACTER = /[*?{[]/;
|
|
451
|
+
var VERSION = /^v?\d+(?:[.\-+][\w.-]*)?$/i;
|
|
452
|
+
var PACKAGE_SCOPE = /^@[^/]+\//;
|
|
453
|
+
var RELEASE_FILE = /(?:^|\/)(?:CHANGELOG\.md|package\.json)$/;
|
|
359
454
|
var MAX_RANGE = 50;
|
|
360
455
|
var phaseIdOf = (cell) => PHASE_ID.exec(cell)?.[1]?.toLowerCase() ?? null;
|
|
361
456
|
var splitOutsideParentheses = (cell, separator) => {
|
|
@@ -417,10 +512,26 @@ var writesOf = (cell, language) => ({
|
|
|
417
512
|
writes: [...cell.matchAll(BACKTICKED)].flatMap((match) => pathOf(match[1] ?? "") ?? []),
|
|
418
513
|
writesLike: writesLikeOf(cell, language)
|
|
419
514
|
});
|
|
420
|
-
var
|
|
421
|
-
|
|
422
|
-
|
|
515
|
+
var releasePattern = (language) => new RegExp(`^${escapeRegExp(PLAN_VOCABULARY[language].releaseMarker)}(?:\\s|$)`, "i");
|
|
516
|
+
var markerEntries = (cell) => cell.split(";").map((entry) => entry.replace(TRAILING_NOTE, "").trim()).filter((entry) => !NO_VALUE.test(entry));
|
|
517
|
+
var packageKey = (name) => name.replace(PACKAGE_SCOPE, "").toLowerCase();
|
|
518
|
+
var releasedPackageOf = (entry) => {
|
|
519
|
+
const name = entry.split(/\s+/).slice(1).filter((word) => !VERSION.test(word)).join(" ");
|
|
520
|
+
return name ? [packageKey(name)] : [];
|
|
521
|
+
};
|
|
522
|
+
var markerCellOf = (cell, language) => {
|
|
523
|
+
const release = releasePattern(language);
|
|
524
|
+
const entries = markerEntries(cell);
|
|
525
|
+
return {
|
|
526
|
+
serialMarkers: entries.filter((entry) => !release.test(entry)),
|
|
527
|
+
releases: entries.filter((entry) => release.test(entry)).flatMap(releasedPackageOf)
|
|
528
|
+
};
|
|
423
529
|
};
|
|
530
|
+
var releasedByFiles = (writes) => writes.flatMap((path) => {
|
|
531
|
+
if (!RELEASE_FILE.test(path)) return [];
|
|
532
|
+
const directory = path.split("/").slice(0, -1);
|
|
533
|
+
return [packageKey(directory.at(-1) ?? ".")];
|
|
534
|
+
});
|
|
424
535
|
var parseDependencyTable = (markdown, language) => {
|
|
425
536
|
const { sections, columns } = PLAN_VOCABULARY[language];
|
|
426
537
|
const { header, rows } = firstTableWithHeader(subsectionLines(markdown, sections.dependencies));
|
|
@@ -436,7 +547,7 @@ var parseDependencyTable = (markdown, language) => {
|
|
|
436
547
|
id,
|
|
437
548
|
...dependsOf(cells[after] ?? "", language),
|
|
438
549
|
...writesOf(cells[writes] ?? "", language),
|
|
439
|
-
|
|
550
|
+
...markerCellOf(cells[marker] ?? "", language)
|
|
440
551
|
}
|
|
441
552
|
];
|
|
442
553
|
});
|
|
@@ -447,6 +558,7 @@ var phasesWithoutDependencyRow = (markdown, language) => {
|
|
|
447
558
|
const listed = new Set(table.map(({ id }) => id));
|
|
448
559
|
return phaseHeadings(markdown, language).filter(({ id }) => !listed.has(id));
|
|
449
560
|
};
|
|
561
|
+
var longestChain = (chains) => chains.reduce((longest, chain) => chain.length > longest.length ? chain : longest, []);
|
|
450
562
|
var mergeSets = (sets) => sets.some((set) => set === null) ? null : sets.flatMap((set) => set ?? []);
|
|
451
563
|
var writeSetOf = (row, byId, chain) => {
|
|
452
564
|
if (chain.includes(row.id)) return null;
|
|
@@ -455,16 +567,19 @@ var writeSetOf = (row, byId, chain) => {
|
|
|
455
567
|
return like === null ? null : [...row.writes, ...like];
|
|
456
568
|
};
|
|
457
569
|
var overlaps = (left, right) => left === "" || right === "" || left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
458
|
-
var
|
|
459
|
-
const
|
|
460
|
-
return first.
|
|
570
|
+
var sharesEntry = (first, second) => {
|
|
571
|
+
const entries = new Set(second.map((entry) => entry.trim().toLowerCase()));
|
|
572
|
+
return first.some((entry) => entries.has(entry.trim().toLowerCase()));
|
|
461
573
|
};
|
|
574
|
+
var sharesMarker = (first, second) => sharesEntry(first.serialMarkers, second.serialMarkers) || sharesEntry(first.releases, second.releases);
|
|
462
575
|
var collides = (first, second) => sharesMarker(first, second) || first.writes.some((path) => second.writes.some((other) => overlaps(path, other)));
|
|
463
576
|
var candidateOf = (row, byId) => {
|
|
464
577
|
const writes = writeSetOf(row, byId, []);
|
|
465
|
-
|
|
578
|
+
if (writes === null || writes.length === 0) return [];
|
|
579
|
+
const releases = [.../* @__PURE__ */ new Set([...row.releases, ...releasedByFiles(writes)])];
|
|
580
|
+
return [{ id: row.id, writes, serialMarkers: row.serialMarkers, releases }];
|
|
466
581
|
};
|
|
467
|
-
var
|
|
582
|
+
var candidateGroups = (rows, doneIds) => {
|
|
468
583
|
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
469
584
|
const ready = rows.filter((row) => !doneIds.has(row.id) && row.external.length === 0 && row.after.every((id) => doneIds.has(id))).flatMap((row) => candidateOf(row, byId));
|
|
470
585
|
const groups = [];
|
|
@@ -473,16 +588,58 @@ var parallelGroups = (rows, doneIds) => {
|
|
|
473
588
|
if (group) group.push(candidate);
|
|
474
589
|
else groups.push([candidate]);
|
|
475
590
|
}
|
|
476
|
-
return groups
|
|
591
|
+
return groups;
|
|
592
|
+
};
|
|
593
|
+
var parallelGroups = (rows, doneIds) => candidateGroups(rows, doneIds).map((group) => group.map(({ id }) => id));
|
|
594
|
+
var phaseWaves = (rows, doneIds) => {
|
|
595
|
+
const waves = [];
|
|
596
|
+
const done = new Set(doneIds);
|
|
597
|
+
let wave = parallelGroups(rows, done)[0];
|
|
598
|
+
while (wave) {
|
|
599
|
+
waves.push(wave);
|
|
600
|
+
for (const id of wave) done.add(id);
|
|
601
|
+
wave = parallelGroups(rows, done)[0];
|
|
602
|
+
}
|
|
603
|
+
return waves;
|
|
604
|
+
};
|
|
605
|
+
var criticalPath = (rows, waves) => {
|
|
606
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
607
|
+
const candidates = new Map(rows.flatMap((row) => candidateOf(row, byId)).map((each) => [each.id, each]));
|
|
608
|
+
const chains = /* @__PURE__ */ new Map();
|
|
609
|
+
const follows = (later, earlier) => {
|
|
610
|
+
const [first, second] = [candidates.get(earlier), candidates.get(later)];
|
|
611
|
+
const chained = first !== void 0 && second !== void 0 && sharesMarker(first, second);
|
|
612
|
+
return chained || (byId.get(later)?.after.includes(earlier) ?? false);
|
|
613
|
+
};
|
|
614
|
+
waves.forEach((wave, index) => {
|
|
615
|
+
const earlier = waves.slice(0, index).flat();
|
|
616
|
+
for (const id of wave) {
|
|
617
|
+
const before = longestChain(earlier.filter((other) => follows(id, other)).map((other) => chains.get(other) ?? []));
|
|
618
|
+
chains.set(id, [...before, id]);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
return longestChain([...chains.values()]);
|
|
477
622
|
};
|
|
478
|
-
var phasesText = (phases) => phases
|
|
623
|
+
var phasesText = (phases) => phaseLabels(phases, " \u2225 ");
|
|
479
624
|
var donePhaseIds = (markdown, language) => new Set(
|
|
480
|
-
parsePlanPhases(markdown, language).filter(
|
|
625
|
+
parsePlanPhases(markdown, language).filter(isPhaseFinished).map(({ id }) => id)
|
|
481
626
|
);
|
|
482
627
|
var parallelGroupsOfPlan = (markdown, language) => {
|
|
483
628
|
const table = parseDependencyTable(markdown, language);
|
|
484
629
|
return table === null ? null : parallelGroups(table, donePhaseIds(markdown, language));
|
|
485
630
|
};
|
|
631
|
+
var wavesOfPlan = (markdown, language) => {
|
|
632
|
+
const table = parseDependencyTable(markdown, language);
|
|
633
|
+
if (table === null) return null;
|
|
634
|
+
const done = donePhaseIds(markdown, language);
|
|
635
|
+
const waves = phaseWaves(table, done);
|
|
636
|
+
const placed = new Set(waves.flat());
|
|
637
|
+
return {
|
|
638
|
+
waves,
|
|
639
|
+
criticalPath: criticalPath(table, waves),
|
|
640
|
+
unplaced: table.filter(({ id }) => !done.has(id) && !placed.has(id)).map(({ id }) => id)
|
|
641
|
+
};
|
|
642
|
+
};
|
|
486
643
|
|
|
487
644
|
// src/plan-format/documentation-gate.ts
|
|
488
645
|
var DOCUMENTATION_WORDS = Object.values(PLAN_VOCABULARY).map(
|
|
@@ -654,93 +811,6 @@ var boardRowsWithoutPhase = (markdown, language, previousLines) => changedOpenLa
|
|
|
654
811
|
({ phases }) => phaseIdsOfCell(phases, language).length === 0
|
|
655
812
|
);
|
|
656
813
|
|
|
657
|
-
// src/plan-format/progress.ts
|
|
658
|
-
var sumPhases = (phases) => ({
|
|
659
|
-
done: phases.reduce((sum, phase) => sum + phase.done, 0),
|
|
660
|
-
total: phases.reduce((sum, phase) => sum + phase.total, 0)
|
|
661
|
-
});
|
|
662
|
-
var phaseById = (phases, id) => phases.find((phase) => phase.id === id);
|
|
663
|
-
var phaseProgress = (phases, id) => {
|
|
664
|
-
const phase = phaseById(phases, id);
|
|
665
|
-
const items = phase?.items ?? [];
|
|
666
|
-
return {
|
|
667
|
-
id,
|
|
668
|
-
title: phase?.title ?? null,
|
|
669
|
-
done: items.filter((item) => item.done).length,
|
|
670
|
-
total: items.length,
|
|
671
|
-
items,
|
|
672
|
-
...phase?.deferred ? { deferred: phase.deferred } : {},
|
|
673
|
-
...phase?.touched ? { touched: true } : {}
|
|
674
|
-
};
|
|
675
|
-
};
|
|
676
|
-
var isOpen = ({ done, total }) => done < total;
|
|
677
|
-
var isWritten = ({ total }) => total > 0;
|
|
678
|
-
var newestWrittenStepIndex = (steps) => steps.reduce((last, { phases }, index) => phases.some(isWritten) ? index : last, -1);
|
|
679
|
-
var newestRowIndex = (steps) => steps.reduce((last, { step }, index) => step.withoutRow ? last : index, -1);
|
|
680
|
-
var isTouched = (phase) => phase.touched === true && isWritten(phase);
|
|
681
|
-
var isTickedUnmerged = (phase) => isTouched(phase) && !isOpen(phase);
|
|
682
|
-
var newestStepWith = (steps, pick) => {
|
|
683
|
-
for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex--) {
|
|
684
|
-
const phase = pick(steps[stepIndex].phases);
|
|
685
|
-
if (phase) return { stepIndex, phaseId: phase.id };
|
|
686
|
-
}
|
|
687
|
-
return null;
|
|
688
|
-
};
|
|
689
|
-
var earlierPhase = (steps, before, id) => {
|
|
690
|
-
const planHref = steps[before].step.planHref;
|
|
691
|
-
for (let stepIndex = before - 1; stepIndex >= 0; stepIndex--) {
|
|
692
|
-
if (steps[stepIndex].step.planHref !== planHref) continue;
|
|
693
|
-
const phase = phaseById(steps[stepIndex].phases, id);
|
|
694
|
-
if (phase) return { phase, current: { stepIndex, phaseId: phase.id } };
|
|
695
|
-
}
|
|
696
|
-
return null;
|
|
697
|
-
};
|
|
698
|
-
var rowPhases = (steps, stepIndex) => {
|
|
699
|
-
const row = steps[stepIndex];
|
|
700
|
-
if (!row) return [];
|
|
701
|
-
const own = row.phases.map((phase) => ({ phase, current: { stepIndex, phaseId: phase.id } }));
|
|
702
|
-
const named = row.step.namedPhaseIds;
|
|
703
|
-
if (!named) return own;
|
|
704
|
-
return named.flatMap((id) => {
|
|
705
|
-
const located = own.find((entry) => entry.phase.id === id) ?? earlierPhase(steps, stepIndex, id);
|
|
706
|
-
return located ? [located] : [];
|
|
707
|
-
});
|
|
708
|
-
};
|
|
709
|
-
var touchedOpenPhaseOf = (phases) => phases.find((phase) => isTouched(phase) && isOpen(phase));
|
|
710
|
-
var lastTickedUnmergedOf = (phases) => phases.filter(isTickedUnmerged).at(-1);
|
|
711
|
-
var newestRowPhase = (steps) => {
|
|
712
|
-
const located = rowPhases(steps, newestRowIndex(steps)).filter(({ phase: phase2 }) => isWritten(phase2));
|
|
713
|
-
const phases = located.map(({ phase: phase2 }) => phase2);
|
|
714
|
-
const phase = touchedOpenPhaseOf(phases) ?? lastTickedUnmergedOf(phases) ?? phases.find(isOpen);
|
|
715
|
-
return located.find((entry) => entry.phase === phase)?.current ?? null;
|
|
716
|
-
};
|
|
717
|
-
var phaseFromTicks = (steps) => {
|
|
718
|
-
const touched = newestStepWith(steps, touchedOpenPhaseOf) ?? newestStepWith(steps, lastTickedUnmergedOf);
|
|
719
|
-
if (touched) return touched;
|
|
720
|
-
const lastIndex = newestWrittenStepIndex(steps);
|
|
721
|
-
const phase = steps[lastIndex]?.phases.find(isOpen);
|
|
722
|
-
return phase ? { stepIndex: lastIndex, phaseId: phase.id } : null;
|
|
723
|
-
};
|
|
724
|
-
var currentPhaseOf = (steps) => newestRowPhase(steps) ?? phaseFromTicks(steps);
|
|
725
|
-
var laneProgress = (lane, plans) => {
|
|
726
|
-
const steps = lane.steps.map((step) => {
|
|
727
|
-
const phases = step.planHref ? plans[step.planHref] : void 0;
|
|
728
|
-
return { step, phases: phases ? step.phaseIds.map((id) => phaseProgress(phases, id)) : [] };
|
|
729
|
-
});
|
|
730
|
-
return { lane, steps, current: currentPhaseOf(steps), ...sumPhases(steps.flatMap((step) => step.phases)) };
|
|
731
|
-
};
|
|
732
|
-
var currentPhase = (progress) => {
|
|
733
|
-
if (!progress.current) return null;
|
|
734
|
-
const { stepIndex, phaseId } = progress.current;
|
|
735
|
-
return phaseById(progress.steps[stepIndex].phases, phaseId) ?? null;
|
|
736
|
-
};
|
|
737
|
-
var activeItem = (phase) => phase.items.find((item) => !item.done) ?? null;
|
|
738
|
-
var shortTitle = (progress) => {
|
|
739
|
-
const phase = currentPhase(progress);
|
|
740
|
-
if (phase) return `${PHASE_ID_PREFIX}${phase.id} \xB7 ${phase.done}/${phase.total}`;
|
|
741
|
-
return progress.total > 0 ? `\u2713 ${progress.done}/${progress.total}` : null;
|
|
742
|
-
};
|
|
743
|
-
|
|
744
814
|
// src/plan-format/sprint-document.ts
|
|
745
815
|
var PHASE_ID_IN_CELL = /^(\d+[a-z]?)\b/;
|
|
746
816
|
var paragraphs = (lines) => lines.join("\n").split(/\n\s*\n/).map((paragraph) => paragraph.trim()).filter(Boolean);
|
|
@@ -781,10 +851,12 @@ var readPhaseDependencies = (planMarkdown, language) => {
|
|
|
781
851
|
};
|
|
782
852
|
export {
|
|
783
853
|
ADDRESS_FORM,
|
|
854
|
+
APPOINTMENT_VOCABULARY,
|
|
784
855
|
APPROVAL_TEXTS,
|
|
785
856
|
LANGUAGE_NAME,
|
|
786
857
|
PHASE_CELL_LIMIT,
|
|
787
858
|
PHASE_ID_PREFIX,
|
|
859
|
+
PLAN_APPROVAL_LABELS,
|
|
788
860
|
PLAN_LANGUAGE,
|
|
789
861
|
PLAN_STATUS,
|
|
790
862
|
PLAN_VOCABULARY,
|
|
@@ -797,6 +869,7 @@ export {
|
|
|
797
869
|
classifyStatus,
|
|
798
870
|
columnModels,
|
|
799
871
|
countDecisions,
|
|
872
|
+
criticalPath,
|
|
800
873
|
currentPhase,
|
|
801
874
|
currentPhaseOf,
|
|
802
875
|
deferralTarget,
|
|
@@ -807,6 +880,8 @@ export {
|
|
|
807
880
|
isCheckboxLine,
|
|
808
881
|
isDocumentationGateItem,
|
|
809
882
|
isOpenCheckboxLine,
|
|
883
|
+
isPhaseFinished,
|
|
884
|
+
isPhaseOpen,
|
|
810
885
|
isPlanLanguage,
|
|
811
886
|
isReviewGateLine,
|
|
812
887
|
isTickedUnmerged,
|
|
@@ -826,6 +901,10 @@ export {
|
|
|
826
901
|
phaseHeadings,
|
|
827
902
|
phaseIdsOfCell,
|
|
828
903
|
phaseIdsOfPlan,
|
|
904
|
+
phaseLabel,
|
|
905
|
+
phaseLabels,
|
|
906
|
+
phaseProgress,
|
|
907
|
+
phaseWaves,
|
|
829
908
|
phasesText,
|
|
830
909
|
phasesWithoutDependencyRow,
|
|
831
910
|
readPhaseDependencies,
|
|
@@ -837,5 +916,6 @@ export {
|
|
|
837
916
|
sprintTitleOf,
|
|
838
917
|
sumPhases,
|
|
839
918
|
tallyPhases,
|
|
919
|
+
wavesOfPlan,
|
|
840
920
|
withoutRepeats
|
|
841
921
|
};
|
package/docs/workflow-dark.svg
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="708" viewBox="0 0 960 708" role="img" aria-labelledby="t d"><title id="t">jackherd in herdr</title><desc id="d">Animated schematic of one herdr window, told in five numbered steps. 1, Lane search works: its state is a teal dot and its progress counts up. 2, It hits an open question: search turns to an amber diamond and sends the question as text. 3, The coordinator asks you: the question appears in the coordinator pane as a choice. 4, You pick: option 1 is selected. 5, The lane works on: the answer goes back to search, which is a teal dot again, and the herd counts one more box.</desc><defs><style>.s{font-family:ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif}.m{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace}@keyframes k1{0%{opacity:1.0}0%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.42}80%{opacity:0.42}81.88%{opacity:1.0}100%{opacity:1.0}}.k1{animation:k1 16s linear infinite both}@keyframes k2{0%{opacity:0.42}40%{opacity:0.42}41.88%{opacity:1.0}81.88%{opacity:1.0}83.75%{opacity:0.42}100%{opacity:0.42}}.k2{animation:k2 16s linear infinite both}@keyframes k3{0%{opacity:0.42}100%{opacity:0.42}}.k3{animation:k3 16s linear infinite both}@keyframes k4{0%{opacity:0}21.25%{opacity:0}23.75%{opacity:1}82.5%{opacity:1}85%{opacity:0}100%{opacity:0}}.k4{animation:k4 16s linear infinite both}@keyframes k5{0%{opacity:0;transform:translate(64px,198px)}23.75%{opacity:0;transform:translate(64px,198px)}23.94%{opacity:1;transform:translate(64px,198px)}24.32%{opacity:1;transform:translate(60.4px,197.5px)}24.9%{opacity:1;transform:translate(57.1px,196.1px)}25.47%{opacity:1;transform:translate(54.2px,193.9px)}26.04%{opacity:1;transform:translate(51.6px,191.1px)}26.61%{opacity:1;transform:translate(49.4px,187.6px)}27.19%{opacity:1;transform:translate(47.6px,183.8px)}27.76%{opacity:1;transform:translate(46.3px,179.6px)}28.33%{opacity:1;transform:translate(45.4px,175.2px)}28.91%{opacity:1;transform:translate(45px,170.7px)}29.48%{opacity:1;transform:translate(45.1px,166.3px)}30.05%{opacity:1;transform:translate(45.8px,162px)}30.63%{opacity:0;transform:translate(47px,158px)}100%{opacity:0;transform:translate(47px,158px)}}.k5{animation:k5 16s linear infinite both}@keyframes k6{0%{opacity:0;transform:translate(47px,158px)}81.25%{opacity:0;transform:translate(47px,158px)}81.44%{opacity:1;transform:translate(47px,158px)}81.67%{opacity:1;transform:translate(45.8px,162px)}82.08%{opacity:1;transform:translate(45.1px,166.3px)}82.5%{opacity:1;transform:translate(45px,170.7px)}82.92%{opacity:1;transform:translate(45.4px,175.2px)}83.33%{opacity:1;transform:translate(46.3px,179.6px)}83.75%{opacity:1;transform:translate(47.6px,183.8px)}84.17%{opacity:1;transform:translate(49.4px,187.6px)}84.58%{opacity:1;transform:translate(51.6px,191.1px)}85%{opacity:1;transform:translate(54.2px,193.9px)}85.42%{opacity:1;transform:translate(57.1px,196.1px)}85.83%{opacity:1;transform:translate(60.4px,197.5px)}86.25%{opacity:0;transform:translate(64px,198px)}100%{opacity:0;transform:translate(64px,198px)}}.k6{animation:k6 16s linear infinite both}@keyframes k7{0%{opacity:0}31.25%{opacity:0}33.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k7{animation:k7 16s linear infinite both}@keyframes k8{0%{opacity:0}41.25%{opacity:0}43.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k8{animation:k8 16s linear infinite both}@keyframes k9{0%{opacity:0}63.75%{opacity:0}66.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k9{animation:k9 16s linear infinite both}@keyframes k10{0%{opacity:0}68.75%{opacity:0}71.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k10{animation:k10 16s linear infinite both}@keyframes k11{0%{opacity:0}81.25%{opacity:0}83.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k11{animation:k11 16s linear infinite both}@keyframes k12{0%{opacity:1}21.25%{opacity:1}23.75%{opacity:0}82.5%{opacity:0}85%{opacity:1}100%{opacity:1}}.k12{animation:k12 16s linear infinite both}@keyframes k13{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k13{animation:k13 16s linear infinite both}@keyframes k14{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k14{animation:k14 16s linear infinite both}@keyframes k15{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k15{animation:k15 16s linear infinite both}@keyframes k16{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k16{animation:k16 16s linear infinite both}@keyframes k17{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k17{animation:k17 16s linear infinite both}@keyframes k18{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k18{animation:k18 16s linear infinite both}@keyframes k19{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k19{animation:k19 16s linear infinite both}@keyframes k20{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k20{animation:k20 16s linear infinite both}@keyframes k21{0%{opacity:1}18.13%{opacity:1}20%{opacity:0}100%{opacity:0}}.k21{animation:k21 16s linear infinite both}@keyframes k22{0%{opacity:1}18.13%{opacity:1}20%{opacity:0.62}100%{opacity:0.62}}.k22{animation:k22 16s linear infinite both}@keyframes k23{0%{opacity:0}20%{opacity:0}21.88%{opacity:1}38.12%{opacity:1}40%{opacity:0}100%{opacity:0}}.k23{animation:k23 16s linear infinite both}@keyframes k24{0%{opacity:0.62}20%{opacity:0.62}21.88%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.62}100%{opacity:0.62}}.k24{animation:k24 16s linear infinite both}@keyframes k25{0%{opacity:0}40%{opacity:0}41.88%{opacity:1}58.12%{opacity:1}60%{opacity:0}100%{opacity:0}}.k25{animation:k25 16s linear infinite both}@keyframes k26{0%{opacity:0.62}40%{opacity:0.62}41.88%{opacity:1.0}58.12%{opacity:1.0}60%{opacity:0.62}100%{opacity:0.62}}.k26{animation:k26 16s linear infinite both}@keyframes k27{0%{opacity:0}60%{opacity:0}61.88%{opacity:1}78.12%{opacity:1}80%{opacity:0}100%{opacity:0}}.k27{animation:k27 16s linear infinite both}@keyframes k28{0%{opacity:0.62}60%{opacity:0.62}61.88%{opacity:1.0}78.12%{opacity:1.0}80%{opacity:0.62}100%{opacity:0.62}}.k28{animation:k28 16s linear infinite both}@keyframes k29{0%{opacity:0}80%{opacity:0}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0}}.k29{animation:k29 16s linear infinite both}@keyframes k30{0%{opacity:0.62}80%{opacity:0.62}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0.62}}.k30{animation:k30 16s linear infinite both}@media (prefers-reduced-motion:reduce){*{animation:none!important}}</style><clipPath id="jack-clip"><rect width="240" height="240" rx="50"/></clipPath><symbol id="jack" viewBox="0 0 240 240"><rect width="240" height="240" rx="50" fill="#212E34"/><g clip-path="url(#jack-clip)"><g transform="translate(34 8)"><path fill="#4A4E51" d="M80 50.5c.2 .1 .2 .1 1 1c-.2 .6-.3 1.3-.5 2c-.7 .4-.7 .4-4 2c-6 3.3-6 3.3-7 6.5c1.2-.4 2.3-.6 3.5-1c.8 0 1.7 0 2.5 0c.2 .1 .2 .1 1 1c-.5 2-.5 2-5.5 6.5c-3 5-3 5-5.5 11c.2 .3 .2 .3 1 2c.2 .3 .3 .7 .5 1c-.7 .7-1.3 1.3-2 2c-1.3-.2-2.6-.3-4-.5c0-1.2 0-2.3 0-3.5c-.2 .1-.2 .1-1.5 .5c-1.2 2.7-2.4 5.3-3.5 8c-1 2.1-2 4.2-3 6.3c-2.5 5.7-2.5 5.7-3 8.2c.5 0 1 0 1.5 0c.1-.8 .4-1.7 .5-2.5c.6 0 1.3 0 2 0c.5 2 1 3.9 1.6 6c1 4 2.2 8 3.4 11.9c2.1 8.4 1.1 15.5-3 23.1c-.6-.2-1.3-.3-2-.5c-.1-1.5-.3-3-.5-4.6c-.3-2.6-.5-5.2-1-7.9c-.5 0-1 0-1.5 0c-.1 1.7-.4 3.3-.5 5c-2.7 10.2-2.7 10.2-6 13.5c-6.1-3.1-9-14.9-11-21c-1.1-8.2 .9-14.7 3.5-22.5c-8.4 8.3-8.3 20.5-9 31.5c-.6-.2-1.3-.3-2-.5c-5.4-8.9-5.1-15-3-25c.5-1 1-2 1.5-3c-3 1.5-3 1.5-6.5 5c-.4 0-.6 0-1 0c.8-6.5 3.2-10.2 7.5-15c.6-.8 .6-.8 3.5-5c2.6-2.6 5.2-5.1 8-7.5c.6-.6 .6-.6 3.5-3.5c-.5-2-.5-2-1-3.5c3.6-6 6.5-9.2 12.5-13c.4-.6 .6-1.3 1-2c-.5-.6-1-1.3-1.5-2c2-3 2-3 4.5-4.5c.5 1 .5 1 1 3.5c2.5-1 2.5-1 8.5-5.5c.3 0 .7 0 1 0c-.1-.4-.1-.4-.5-2.5c4.3-3.3 10-2.5 15-1zM153 69.5c1.2 .2 2.3 .3 3.5 .5c.3 .7 .7 1.3 1 2c.3 0 .7 0 1 0c.5 1.2 1 2.3 1.5 3.5c3.4 7.6 6.2 13.7 12.2 19.6c3.3 3.4 3.3 3.4 3.8 5.9c-.2 .2-.3 .3-.5 .5c.1 .7 .1 .7 .5 4.5c-.5 .3-1 .7-1.5 1c-.8-.3-1.7-.7-2.5-1c.3 .7 .7 1.3 1 2c1.2 5.8 1.4 10 1 16c-.7 0-1.3 0-2 0c-.4-1.3-.8-2.7-1.3-4.1c-2.1-6.1-5.2-11.4-8.7-16.9c-.2-.8-.2-.8-1.5-5c-.3-.6-.3-.6-2.1-3.5c-1.9-3.5-1.9-3.5-2.9-8.5c.7 0 1.3 0 2 0c.5 .5 1 1 1.5 1.5c-.8-2.3-1.7-4.6-2.5-7c-.7-2-1.3-4-2-6c-.7-1-1.3-2-2-3c.2-.7 .3-1.3 .5-2zM86.5 100.5c1 0 2 0 3 0c1.3 .8 2.7 1.7 4 2.5c-2.5 2.5-2.5 2.5-8 5.5c-5.5 4.3-8.6 7-10 14c-.8 2.2-1.7 4.3-2.5 6.5c-1.2 4.6-.5 9.3 0 14c-.5 0-1 0-1.5 0c-.2 2-.3 3.9-.5 6c-.7 0-1.3 0-2 0c-.3-.6-.3-.6-2-3.5c-1-.8-2-1.7-3-2.5c-.5-1.5-.5-1.5 1.5-8.5c.3-10.3-1-18.2-4-28c0-4 0-4 1-6c2 2 2 2 3 6.5c2.5-.5 2.5-.5 8-4c9.8-3.5 9.8-3.5 13-2.5zM43 161.5c2.5 2.5 2.5 2.5 3 5c-1.5 2.5-1.5 2.5-4 5c.5 3.5 .5 3.5 4 6.5c-.1 1.2-.4 2.3-.5 3.5c-5.3 3.5-5.3 3.5-8.5 3.5c-6-2.5-6-2.5-11.5-7c0-3 0-3 4-8c9.5-8.5 9.5-8.5 13.5-8.5z"/><path fill="#9B9D9E" d="M142.5 60.5c.5 1 .5 1-.5 4.5c1.2 1.5 2.3 3 3.5 4.5c2 4.5 2 4.5 2.5 9.5c-.5 .2-1 .3-1.5 .5c-.1 .2-.1 .2-.5 1c-2-1-3.9-2-6-3c-.8-.3-1.7-.7-2.5-1c-.3-.7-.7-1.3-1-2c.3-.5 .7-1 1-1.5c-.2-.7-.3-1.3-.5-2c-3.5 .5-3.5 .5-7.5 4c-.5 3-.5 3 0 5.5c-2 4-2 4-3.5 5.5c-.3 0-.7 0-1 0c0 .7 0 1.3 0 2c-2-1-2-1-2.5-2.5c.3-13.7 .3-13.7 4.8-18.5c4.1-3.9 6.3-4.8 11.7-6c1-.5 1-.5 3.5-.5zM98 70.5c.5 .3 1 .7 1.5 1c1.3 .3 2.7 .7 4 1c4.4 5.8 6.5 10.3 6.5 17.5c-2 2-2 2-5 2.5c-.3 .2-.3 .2-2 1c-.2-.2-.2-.2-1.5-1c.5-3 .5-3 1-5c-.7-.8-1.3-1.7-2-2.5c-.2-1.7-.3-3.3-.5-5c-4.5-2.7-8.5-3-13.5-2.5c-.1 .3-.1 .3-.5 1.5c1.3 .8 2.7 1.7 4 2.5c-.1 .4-.1 .4-.5 2.5c-2.3 1.2-4.6 2.3-7 3.5c-5.5 5.5-5.5 5.5-6.5 7c0 .5 0 1 0 1.5c.9-.1 .9-.1 5.5-.5c1 1.3 2 2.7 3 4c-.2 .5-.3 1-.5 1.5c-1.7 .5-3.2 1-5 1.6c-5.5 1.9-5.5 1.9-11 5.4c-.4 .1-.4 .1-2.5 .5c-1.5-1.5-1.5-1.5-2.5-6c-.4-.3-.6-.7-1-1c0-6.7 .9-11.7 3-18c.2-.6 .2-.6 1-3.5c7.8-9.7 20.5-14.1 32-9.5z"/><path fill="#FAF9F8" d="M103.5 40c0 .4 0 .6 0 1c2.2-.1 4.3-.4 6.5-.5c7 0 7 0 9.5 1c-.2 .6-.3 1.3-.5 2c-1.8 .6-3.7 1.3-5.5 2c-.2 .5-.3 1-.5 1.5c.7-.1 1.3-.4 2-.5c13.4-.3 13.4-.3 19.5 3.5c-.3 .6-.7 1.3-1 2c-1 0-2 0-3 0c0 .5 0 1 0 1.5c1.2 .1 1.2 .1 7 .5c3.5 2 3.5 2 5.5 4.5c0 1 0 2 0 3c-3.3 .9-6.7 1.6-10 2.5c-7.5 5-7.5 5-8.5 9c-.3 4.5-.4 9-.5 13.5c1.5 1 1.5 1 7.5 2.5c.9 .6 .9 .6 5.5 3.5c2 1 2 1 9 2.5c7.9 4 11.3 8.3 15 16.5c0 .5 0 1 0 1.5c-1 .2-2 .3-3 .5c.5 1.8 1 3.7 1.5 5.5c.3 8.5-.8 13.6-6 20.5c-.7-.2-1.3-.3-2-.5c0-1.7 0-3.3 0-5c-.7 .2-1.3 .3-2 .5c-1.3 2.7-2.7 5.3-4 8c-3.2 3.7-6.4 6.2-11 8c-1-1-1-1-1.5-3c-2.5 1.3-4.8 2.9-7 4.5c-6.3 2.8-10.8 3.2-17.6 1c-1.3-.5-2.6-1-3.9-1.5c.2 1.2 .3 2.3 .5 3.5c-.2 .3-.3 .7-.5 1c-5.9 0-9-1.8-14-5c-3-3.5-3-3.5-3-4.5c-.7 0-1.3 0-2 0c.5 2.2 1 4.3 1.5 6.5c-.3 .3-.7 .7-1 1c-4.5-.9-7.6-3.6-11-6.5c-4-6-4-9.3-4-16.5c2.5-7.5 2.5-7.5 3.5-9.5c.3-1.7 .7-3.3 1-5c6.4-8 14.7-13 24.5-16c6.4-6.3 6.4-6.3 8.5-10.5c0-6-1.6-9.8-4.5-15c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-1.3-.7-2.7-1.3-4-2c-5.7-1.9-10.2-.3-15.5 1.5c-10 5.5-10 5.5-13 10c-.3-.2-.3-.2-2-1c1.3-6.8 4.6-11.7 9.5-16.5c.5-.3 1-.6 1.5-1c.2-.5 .3-1 .5-1.5c-1 .1-1 .1-6 1c-.3-.4-.7-.6-1-1c.5-2 .5-2 4-6c5-2.5 5-2.5 7.5-3c0-.5 0-1 0-1.5c-10.5-1.5-18.5 1-27 7c-.5 .9-1 1.6-1.5 2.5c-.9 .6-.9 .6-5.5 4c-4 3.5-7.6 7-11 11c-4.6 3.8-8.7 6.8-14 9.5c-.7-.3-1.3-.7-2-1c.2-.7 .3-1.3 .5-2c1.8-1 3.7-2 5.5-3c5.2-4.2 9.7-8.5 14-13.5c3.1-2.8 6.2-5.5 9.5-8c1.3-1.3 2.6-2.6 4-4c5.6-3.6 10.4-6.1 17-7c.5-1 1-2 1.5-3c7.2-2.4 12.5-2.2 20-.5c.2 .1 .2 .1 1 .5c3.5 0 3.5 0 8.5-3.5c1-.5 1-.5 3-.5zM118 92.5c-1 1.2-2 2.3-3 3.5c.5 2.5 .5 2.5 3 5c1.8 .8 3.7 1.7 5.5 2.5c.5 1 .5 1 .5 4.5c-4 4.8-6.1 5-12.5 6c0 .7 0 1.3 0 2c4.7 1.5 7.7 .2 12-1.5c1.2-.7 2.3-1.3 3.5-2c1.3 .3 2.7 .7 4 1c3.5 0 3.5 0 5.5-1.5c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-2.5-2-2.5-2-3-5.5c2-3 2-3 4-5.5c.5-3.5 .5-3.5-1.5-6.5c-4.3-2.2-8.2-1-12.5 .5zM146 58.5c5.8 1.1 8.3 3.9 12.5 8c3.7 5.9 4.4 7.3 5 14c2.8 6.2 5.4 10 10.5 14.5c5.2 5.2 7.2 10.3 9 17.5c0 3.5 0 3.5-2 11c-.2 1.3-.2 1.3-1 7.5c-3.8 10.7-3.8 10.7-8.5 13.5c-.5 .2-.5 .2-3 1c-1 1.3-2 2.7-3 4c-4.5 3-4.5 3-10.5 5c-2.7-.6-5.3-1.3-8-2c-1.7 1.2-3.3 2.3-5 3.5c-2 1-2 1-4 .5c.5-1.5 .5-1.5 6-5c.3-.3 .3-.3 2-2c2.4 0 4.9 0 7.5 0c.5 .5 1 1 1.5 1.5c4.3-.6 7.1-2.3 10.5-5c3.5-5.5 3.5-5.5 4-7c1.5 0 2.9 0 4.5 0c4.3-8.7 4.6-14 2.5-23.5c-.3-1-.7-2-1-3c.3 .1 .3 .1 2 .5c1.5 2.5 1.5 2.5 1.5 4c.5-.2 1-.3 1.5-.5c-.8-10-4.3-16-11.4-22.8c-3.7-3.8-5.5-6.9-7.6-11.7c-.7-1.5-1.5-2.9-2.3-4.4c-1.7-3.6-1.7-3.6-1.7-5.6c.3-.2 .7-.3 1-.5c-.5-3-.5-3-4.5-7c-2-1-3.9-2-6-3c-.7-.4-1.3-.6-2-1c0-.6 0-1.3 0-2zM33.5 83.5c-1 4-5.1 6.4-8 9c-8.6 8.1-14.6 17.2-15.2 29.2c.3 8.1 .3 8.1 2.2 11.8c.5 0 1 0 1.5 0c.4-2.9 .6-6 1-9c.6 0 1.3 0 2 0c.2 1.3 .4 2.7 .6 4.1c1.8 9.3 5.3 14.8 12.9 20.4c2.3 1.2 4.6 2.3 7 3.5c.7 .6 .7 .6 4.5 3.5c3.5 0 3.5 0 8.5-1.5c1 0 2 0 3 0c-1 2-1 2-2.5 3.5c-6.5 1.7-9.6 .9-16-1.5c-1.1-1-2.3-2-3.5-3c-1.5-.7-2.9-1.3-4.5-2c-3.9-3.4-6.9-6.5-9.5-11c-1.1-.5-2.3-1-3.5-1.5c-5.9-6.7-8-12.7-8-21.5c2.4-12.2 7.2-19.8 16.5-28c4.5-4 4.5-4 6-5.5c3.5-1 3.5-1 5-.5zM50.5 172c2.5 1 2.5 1 7 6.5c6 6.2 12 11.8 18.8 17.1c5.7 4.5 11.3 9.1 16.7 13.9c1.3 1.2 2.7 2.3 4 3.5c.7 .7 1.3 1.3 2 2c-.2 .8-.3 1.7-.5 2.5c-6 2.9-11.7 4-18.5 4c-2.6-2.6-4.3-5.5-5.5-9c-.3 .1-.3 .1-1.5 .5c0 2.3 0 4.6 0 7c-.3 .5-.7 1-1 1.5c-13.3 0-24.6-4.6-34.5-13.5c-4.5-4.5-4.5-4.5-8.5-11c.5-2.5 .5-2.5 4.5-7c5-3 5-3 8-3.5c.5 .3 1 .7 1.5 1c2-.5 2-.5 4-2c.9-4.2 1.7-8.3 2.5-12.5c.4-.3 .6-.7 1-1z"/><path fill="#03B5BD" d="M58.5 149.5c2.5 .5 2.5 .5 7 3.5c9.8 5.2 18.9 7.9 30 8.5c2.2 .5 4.3 1 6.5 1.5c7 0 7 0 9-.5c2.3-.2 4.6-.3 7-.5c3.2-.9 6.3-1.9 9.5-3c1.3-.2 2.7-.3 4-.5c.2 .2 .3 .3 .5 .5c-.8 6.3-4.1 10.2-8 15c-.2 .5-.3 1-.5 1.5c1.7-1 3.3-2 5-3c.7 .2 1.3 .3 2 .5c-3 12.2-9.3 20.3-17.5 29.5c-.7 .7-.7 .7-4 4.5c-1 .8-2 1.7-3 2.5c-3.5-.5-3.5-.5-8-5c-2.3-1.8-4.7-3.7-7-5.5c-2.7-2.2-5.3-4.3-8-6.5c-.8-.6-.8-.6-4.5-3.5c-11-9.3-20.7-18.8-28.5-31c3-4 3-4 8.5-8.5z"/><ellipse cx="94" cy="94" rx="6.5" ry="6.5" fill="#212E34"/><ellipse cx="135" cy="85.6" rx="6.2" ry="5.8" fill="#212E34"/><g fill="#FAF9F8"><circle cx="91.5" cy="91.5" r="2.1"/><circle cx="134.6" cy="84.3" r="2"/></g><ellipse cx="124" cy="93.8" rx="3.4" ry="1.1" fill="#9B9D9E" transform="rotate(-6 124 93.8)"/></g></g><rect x="5" y="5" width="230" height="230" rx="45" fill="none" stroke="#5B6B73" stroke-width="10"/></symbol></defs>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="724" viewBox="0 0 960 724" role="img" aria-labelledby="t d"><title id="t">jackherd in herdr</title><desc id="d">Animated schematic of one herdr window, told in five numbered steps. 1, Lane search works: its state is a teal dot and its progress counts up. 2, It hits an open question: search turns to an amber diamond and sends the question as text. 3, The coordinator asks you: the question appears in the coordinator pane as a choice. 4, You pick: option 1 is selected. 5, The lane works on: the answer goes back to search, which is a teal dot again, and the herd counts one more box.</desc><defs><style>.s{font-family:ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif}.m{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace}@keyframes k1{0%{opacity:1.0}0%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.42}80%{opacity:0.42}81.88%{opacity:1.0}100%{opacity:1.0}}.k1{animation:k1 16s linear infinite both}@keyframes k2{0%{opacity:0.42}40%{opacity:0.42}41.88%{opacity:1.0}81.88%{opacity:1.0}83.75%{opacity:0.42}100%{opacity:0.42}}.k2{animation:k2 16s linear infinite both}@keyframes k3{0%{opacity:0.42}100%{opacity:0.42}}.k3{animation:k3 16s linear infinite both}@keyframes k4{0%{opacity:0}21.25%{opacity:0}23.75%{opacity:1}82.5%{opacity:1}85%{opacity:0}100%{opacity:0}}.k4{animation:k4 16s linear infinite both}@keyframes k5{0%{opacity:0;transform:translate(64px,198px)}23.75%{opacity:0;transform:translate(64px,198px)}23.94%{opacity:1;transform:translate(64px,198px)}24.32%{opacity:1;transform:translate(60.4px,197.5px)}24.9%{opacity:1;transform:translate(57.1px,196.1px)}25.47%{opacity:1;transform:translate(54.2px,193.9px)}26.04%{opacity:1;transform:translate(51.6px,191.1px)}26.61%{opacity:1;transform:translate(49.4px,187.6px)}27.19%{opacity:1;transform:translate(47.6px,183.8px)}27.76%{opacity:1;transform:translate(46.3px,179.6px)}28.33%{opacity:1;transform:translate(45.4px,175.2px)}28.91%{opacity:1;transform:translate(45px,170.7px)}29.48%{opacity:1;transform:translate(45.1px,166.3px)}30.05%{opacity:1;transform:translate(45.8px,162px)}30.63%{opacity:0;transform:translate(47px,158px)}100%{opacity:0;transform:translate(47px,158px)}}.k5{animation:k5 16s linear infinite both}@keyframes k6{0%{opacity:0;transform:translate(47px,158px)}81.25%{opacity:0;transform:translate(47px,158px)}81.44%{opacity:1;transform:translate(47px,158px)}81.67%{opacity:1;transform:translate(45.8px,162px)}82.08%{opacity:1;transform:translate(45.1px,166.3px)}82.5%{opacity:1;transform:translate(45px,170.7px)}82.92%{opacity:1;transform:translate(45.4px,175.2px)}83.33%{opacity:1;transform:translate(46.3px,179.6px)}83.75%{opacity:1;transform:translate(47.6px,183.8px)}84.17%{opacity:1;transform:translate(49.4px,187.6px)}84.58%{opacity:1;transform:translate(51.6px,191.1px)}85%{opacity:1;transform:translate(54.2px,193.9px)}85.42%{opacity:1;transform:translate(57.1px,196.1px)}85.83%{opacity:1;transform:translate(60.4px,197.5px)}86.25%{opacity:0;transform:translate(64px,198px)}100%{opacity:0;transform:translate(64px,198px)}}.k6{animation:k6 16s linear infinite both}@keyframes k7{0%{opacity:0}31.25%{opacity:0}33.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k7{animation:k7 16s linear infinite both}@keyframes k8{0%{opacity:0}41.25%{opacity:0}43.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k8{animation:k8 16s linear infinite both}@keyframes k9{0%{opacity:0}63.75%{opacity:0}66.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k9{animation:k9 16s linear infinite both}@keyframes k10{0%{opacity:0}68.75%{opacity:0}71.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k10{animation:k10 16s linear infinite both}@keyframes k11{0%{opacity:0}81.25%{opacity:0}83.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k11{animation:k11 16s linear infinite both}@keyframes k12{0%{opacity:1}21.25%{opacity:1}23.75%{opacity:0}82.5%{opacity:0}85%{opacity:1}100%{opacity:1}}.k12{animation:k12 16s linear infinite both}@keyframes k13{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k13{animation:k13 16s linear infinite both}@keyframes k14{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k14{animation:k14 16s linear infinite both}@keyframes k15{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k15{animation:k15 16s linear infinite both}@keyframes k16{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k16{animation:k16 16s linear infinite both}@keyframes k17{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k17{animation:k17 16s linear infinite both}@keyframes k18{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k18{animation:k18 16s linear infinite both}@keyframes k19{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k19{animation:k19 16s linear infinite both}@keyframes k20{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k20{animation:k20 16s linear infinite both}@keyframes k21{0%{opacity:1}18.13%{opacity:1}20%{opacity:0}100%{opacity:0}}.k21{animation:k21 16s linear infinite both}@keyframes k22{0%{opacity:1}18.13%{opacity:1}20%{opacity:0.62}100%{opacity:0.62}}.k22{animation:k22 16s linear infinite both}@keyframes k23{0%{opacity:0}20%{opacity:0}21.88%{opacity:1}38.12%{opacity:1}40%{opacity:0}100%{opacity:0}}.k23{animation:k23 16s linear infinite both}@keyframes k24{0%{opacity:0.62}20%{opacity:0.62}21.88%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.62}100%{opacity:0.62}}.k24{animation:k24 16s linear infinite both}@keyframes k25{0%{opacity:0}40%{opacity:0}41.88%{opacity:1}58.12%{opacity:1}60%{opacity:0}100%{opacity:0}}.k25{animation:k25 16s linear infinite both}@keyframes k26{0%{opacity:0.62}40%{opacity:0.62}41.88%{opacity:1.0}58.12%{opacity:1.0}60%{opacity:0.62}100%{opacity:0.62}}.k26{animation:k26 16s linear infinite both}@keyframes k27{0%{opacity:0}60%{opacity:0}61.88%{opacity:1}78.12%{opacity:1}80%{opacity:0}100%{opacity:0}}.k27{animation:k27 16s linear infinite both}@keyframes k28{0%{opacity:0.62}60%{opacity:0.62}61.88%{opacity:1.0}78.12%{opacity:1.0}80%{opacity:0.62}100%{opacity:0.62}}.k28{animation:k28 16s linear infinite both}@keyframes k29{0%{opacity:0}80%{opacity:0}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0}}.k29{animation:k29 16s linear infinite both}@keyframes k30{0%{opacity:0.62}80%{opacity:0.62}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0.62}}.k30{animation:k30 16s linear infinite both}@media (prefers-reduced-motion:reduce){*{animation:none!important}}</style><clipPath id="jack-clip"><rect width="240" height="240" rx="50"/></clipPath><symbol id="jack" viewBox="0 0 240 240"><rect width="240" height="240" rx="50" fill="#212E34"/><g clip-path="url(#jack-clip)"><g transform="translate(34 8)"><path fill="#4A4E51" d="M80 50.5c.2 .1 .2 .1 1 1c-.2 .6-.3 1.3-.5 2c-.7 .4-.7 .4-4 2c-6 3.3-6 3.3-7 6.5c1.2-.4 2.3-.6 3.5-1c.8 0 1.7 0 2.5 0c.2 .1 .2 .1 1 1c-.5 2-.5 2-5.5 6.5c-3 5-3 5-5.5 11c.2 .3 .2 .3 1 2c.2 .3 .3 .7 .5 1c-.7 .7-1.3 1.3-2 2c-1.3-.2-2.6-.3-4-.5c0-1.2 0-2.3 0-3.5c-.2 .1-.2 .1-1.5 .5c-1.2 2.7-2.4 5.3-3.5 8c-1 2.1-2 4.2-3 6.3c-2.5 5.7-2.5 5.7-3 8.2c.5 0 1 0 1.5 0c.1-.8 .4-1.7 .5-2.5c.6 0 1.3 0 2 0c.5 2 1 3.9 1.6 6c1 4 2.2 8 3.4 11.9c2.1 8.4 1.1 15.5-3 23.1c-.6-.2-1.3-.3-2-.5c-.1-1.5-.3-3-.5-4.6c-.3-2.6-.5-5.2-1-7.9c-.5 0-1 0-1.5 0c-.1 1.7-.4 3.3-.5 5c-2.7 10.2-2.7 10.2-6 13.5c-6.1-3.1-9-14.9-11-21c-1.1-8.2 .9-14.7 3.5-22.5c-8.4 8.3-8.3 20.5-9 31.5c-.6-.2-1.3-.3-2-.5c-5.4-8.9-5.1-15-3-25c.5-1 1-2 1.5-3c-3 1.5-3 1.5-6.5 5c-.4 0-.6 0-1 0c.8-6.5 3.2-10.2 7.5-15c.6-.8 .6-.8 3.5-5c2.6-2.6 5.2-5.1 8-7.5c.6-.6 .6-.6 3.5-3.5c-.5-2-.5-2-1-3.5c3.6-6 6.5-9.2 12.5-13c.4-.6 .6-1.3 1-2c-.5-.6-1-1.3-1.5-2c2-3 2-3 4.5-4.5c.5 1 .5 1 1 3.5c2.5-1 2.5-1 8.5-5.5c.3 0 .7 0 1 0c-.1-.4-.1-.4-.5-2.5c4.3-3.3 10-2.5 15-1zM153 69.5c1.2 .2 2.3 .3 3.5 .5c.3 .7 .7 1.3 1 2c.3 0 .7 0 1 0c.5 1.2 1 2.3 1.5 3.5c3.4 7.6 6.2 13.7 12.2 19.6c3.3 3.4 3.3 3.4 3.8 5.9c-.2 .2-.3 .3-.5 .5c.1 .7 .1 .7 .5 4.5c-.5 .3-1 .7-1.5 1c-.8-.3-1.7-.7-2.5-1c.3 .7 .7 1.3 1 2c1.2 5.8 1.4 10 1 16c-.7 0-1.3 0-2 0c-.4-1.3-.8-2.7-1.3-4.1c-2.1-6.1-5.2-11.4-8.7-16.9c-.2-.8-.2-.8-1.5-5c-.3-.6-.3-.6-2.1-3.5c-1.9-3.5-1.9-3.5-2.9-8.5c.7 0 1.3 0 2 0c.5 .5 1 1 1.5 1.5c-.8-2.3-1.7-4.6-2.5-7c-.7-2-1.3-4-2-6c-.7-1-1.3-2-2-3c.2-.7 .3-1.3 .5-2zM86.5 100.5c1 0 2 0 3 0c1.3 .8 2.7 1.7 4 2.5c-2.5 2.5-2.5 2.5-8 5.5c-5.5 4.3-8.6 7-10 14c-.8 2.2-1.7 4.3-2.5 6.5c-1.2 4.6-.5 9.3 0 14c-.5 0-1 0-1.5 0c-.2 2-.3 3.9-.5 6c-.7 0-1.3 0-2 0c-.3-.6-.3-.6-2-3.5c-1-.8-2-1.7-3-2.5c-.5-1.5-.5-1.5 1.5-8.5c.3-10.3-1-18.2-4-28c0-4 0-4 1-6c2 2 2 2 3 6.5c2.5-.5 2.5-.5 8-4c9.8-3.5 9.8-3.5 13-2.5zM43 161.5c2.5 2.5 2.5 2.5 3 5c-1.5 2.5-1.5 2.5-4 5c.5 3.5 .5 3.5 4 6.5c-.1 1.2-.4 2.3-.5 3.5c-5.3 3.5-5.3 3.5-8.5 3.5c-6-2.5-6-2.5-11.5-7c0-3 0-3 4-8c9.5-8.5 9.5-8.5 13.5-8.5z"/><path fill="#9B9D9E" d="M142.5 60.5c.5 1 .5 1-.5 4.5c1.2 1.5 2.3 3 3.5 4.5c2 4.5 2 4.5 2.5 9.5c-.5 .2-1 .3-1.5 .5c-.1 .2-.1 .2-.5 1c-2-1-3.9-2-6-3c-.8-.3-1.7-.7-2.5-1c-.3-.7-.7-1.3-1-2c.3-.5 .7-1 1-1.5c-.2-.7-.3-1.3-.5-2c-3.5 .5-3.5 .5-7.5 4c-.5 3-.5 3 0 5.5c-2 4-2 4-3.5 5.5c-.3 0-.7 0-1 0c0 .7 0 1.3 0 2c-2-1-2-1-2.5-2.5c.3-13.7 .3-13.7 4.8-18.5c4.1-3.9 6.3-4.8 11.7-6c1-.5 1-.5 3.5-.5zM98 70.5c.5 .3 1 .7 1.5 1c1.3 .3 2.7 .7 4 1c4.4 5.8 6.5 10.3 6.5 17.5c-2 2-2 2-5 2.5c-.3 .2-.3 .2-2 1c-.2-.2-.2-.2-1.5-1c.5-3 .5-3 1-5c-.7-.8-1.3-1.7-2-2.5c-.2-1.7-.3-3.3-.5-5c-4.5-2.7-8.5-3-13.5-2.5c-.1 .3-.1 .3-.5 1.5c1.3 .8 2.7 1.7 4 2.5c-.1 .4-.1 .4-.5 2.5c-2.3 1.2-4.6 2.3-7 3.5c-5.5 5.5-5.5 5.5-6.5 7c0 .5 0 1 0 1.5c.9-.1 .9-.1 5.5-.5c1 1.3 2 2.7 3 4c-.2 .5-.3 1-.5 1.5c-1.7 .5-3.2 1-5 1.6c-5.5 1.9-5.5 1.9-11 5.4c-.4 .1-.4 .1-2.5 .5c-1.5-1.5-1.5-1.5-2.5-6c-.4-.3-.6-.7-1-1c0-6.7 .9-11.7 3-18c.2-.6 .2-.6 1-3.5c7.8-9.7 20.5-14.1 32-9.5z"/><path fill="#FAF9F8" d="M103.5 40c0 .4 0 .6 0 1c2.2-.1 4.3-.4 6.5-.5c7 0 7 0 9.5 1c-.2 .6-.3 1.3-.5 2c-1.8 .6-3.7 1.3-5.5 2c-.2 .5-.3 1-.5 1.5c.7-.1 1.3-.4 2-.5c13.4-.3 13.4-.3 19.5 3.5c-.3 .6-.7 1.3-1 2c-1 0-2 0-3 0c0 .5 0 1 0 1.5c1.2 .1 1.2 .1 7 .5c3.5 2 3.5 2 5.5 4.5c0 1 0 2 0 3c-3.3 .9-6.7 1.6-10 2.5c-7.5 5-7.5 5-8.5 9c-.3 4.5-.4 9-.5 13.5c1.5 1 1.5 1 7.5 2.5c.9 .6 .9 .6 5.5 3.5c2 1 2 1 9 2.5c7.9 4 11.3 8.3 15 16.5c0 .5 0 1 0 1.5c-1 .2-2 .3-3 .5c.5 1.8 1 3.7 1.5 5.5c.3 8.5-.8 13.6-6 20.5c-.7-.2-1.3-.3-2-.5c0-1.7 0-3.3 0-5c-.7 .2-1.3 .3-2 .5c-1.3 2.7-2.7 5.3-4 8c-3.2 3.7-6.4 6.2-11 8c-1-1-1-1-1.5-3c-2.5 1.3-4.8 2.9-7 4.5c-6.3 2.8-10.8 3.2-17.6 1c-1.3-.5-2.6-1-3.9-1.5c.2 1.2 .3 2.3 .5 3.5c-.2 .3-.3 .7-.5 1c-5.9 0-9-1.8-14-5c-3-3.5-3-3.5-3-4.5c-.7 0-1.3 0-2 0c.5 2.2 1 4.3 1.5 6.5c-.3 .3-.7 .7-1 1c-4.5-.9-7.6-3.6-11-6.5c-4-6-4-9.3-4-16.5c2.5-7.5 2.5-7.5 3.5-9.5c.3-1.7 .7-3.3 1-5c6.4-8 14.7-13 24.5-16c6.4-6.3 6.4-6.3 8.5-10.5c0-6-1.6-9.8-4.5-15c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-1.3-.7-2.7-1.3-4-2c-5.7-1.9-10.2-.3-15.5 1.5c-10 5.5-10 5.5-13 10c-.3-.2-.3-.2-2-1c1.3-6.8 4.6-11.7 9.5-16.5c.5-.3 1-.6 1.5-1c.2-.5 .3-1 .5-1.5c-1 .1-1 .1-6 1c-.3-.4-.7-.6-1-1c.5-2 .5-2 4-6c5-2.5 5-2.5 7.5-3c0-.5 0-1 0-1.5c-10.5-1.5-18.5 1-27 7c-.5 .9-1 1.6-1.5 2.5c-.9 .6-.9 .6-5.5 4c-4 3.5-7.6 7-11 11c-4.6 3.8-8.7 6.8-14 9.5c-.7-.3-1.3-.7-2-1c.2-.7 .3-1.3 .5-2c1.8-1 3.7-2 5.5-3c5.2-4.2 9.7-8.5 14-13.5c3.1-2.8 6.2-5.5 9.5-8c1.3-1.3 2.6-2.6 4-4c5.6-3.6 10.4-6.1 17-7c.5-1 1-2 1.5-3c7.2-2.4 12.5-2.2 20-.5c.2 .1 .2 .1 1 .5c3.5 0 3.5 0 8.5-3.5c1-.5 1-.5 3-.5zM118 92.5c-1 1.2-2 2.3-3 3.5c.5 2.5 .5 2.5 3 5c1.8 .8 3.7 1.7 5.5 2.5c.5 1 .5 1 .5 4.5c-4 4.8-6.1 5-12.5 6c0 .7 0 1.3 0 2c4.7 1.5 7.7 .2 12-1.5c1.2-.7 2.3-1.3 3.5-2c1.3 .3 2.7 .7 4 1c3.5 0 3.5 0 5.5-1.5c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-2.5-2-2.5-2-3-5.5c2-3 2-3 4-5.5c.5-3.5 .5-3.5-1.5-6.5c-4.3-2.2-8.2-1-12.5 .5zM146 58.5c5.8 1.1 8.3 3.9 12.5 8c3.7 5.9 4.4 7.3 5 14c2.8 6.2 5.4 10 10.5 14.5c5.2 5.2 7.2 10.3 9 17.5c0 3.5 0 3.5-2 11c-.2 1.3-.2 1.3-1 7.5c-3.8 10.7-3.8 10.7-8.5 13.5c-.5 .2-.5 .2-3 1c-1 1.3-2 2.7-3 4c-4.5 3-4.5 3-10.5 5c-2.7-.6-5.3-1.3-8-2c-1.7 1.2-3.3 2.3-5 3.5c-2 1-2 1-4 .5c.5-1.5 .5-1.5 6-5c.3-.3 .3-.3 2-2c2.4 0 4.9 0 7.5 0c.5 .5 1 1 1.5 1.5c4.3-.6 7.1-2.3 10.5-5c3.5-5.5 3.5-5.5 4-7c1.5 0 2.9 0 4.5 0c4.3-8.7 4.6-14 2.5-23.5c-.3-1-.7-2-1-3c.3 .1 .3 .1 2 .5c1.5 2.5 1.5 2.5 1.5 4c.5-.2 1-.3 1.5-.5c-.8-10-4.3-16-11.4-22.8c-3.7-3.8-5.5-6.9-7.6-11.7c-.7-1.5-1.5-2.9-2.3-4.4c-1.7-3.6-1.7-3.6-1.7-5.6c.3-.2 .7-.3 1-.5c-.5-3-.5-3-4.5-7c-2-1-3.9-2-6-3c-.7-.4-1.3-.6-2-1c0-.6 0-1.3 0-2zM33.5 83.5c-1 4-5.1 6.4-8 9c-8.6 8.1-14.6 17.2-15.2 29.2c.3 8.1 .3 8.1 2.2 11.8c.5 0 1 0 1.5 0c.4-2.9 .6-6 1-9c.6 0 1.3 0 2 0c.2 1.3 .4 2.7 .6 4.1c1.8 9.3 5.3 14.8 12.9 20.4c2.3 1.2 4.6 2.3 7 3.5c.7 .6 .7 .6 4.5 3.5c3.5 0 3.5 0 8.5-1.5c1 0 2 0 3 0c-1 2-1 2-2.5 3.5c-6.5 1.7-9.6 .9-16-1.5c-1.1-1-2.3-2-3.5-3c-1.5-.7-2.9-1.3-4.5-2c-3.9-3.4-6.9-6.5-9.5-11c-1.1-.5-2.3-1-3.5-1.5c-5.9-6.7-8-12.7-8-21.5c2.4-12.2 7.2-19.8 16.5-28c4.5-4 4.5-4 6-5.5c3.5-1 3.5-1 5-.5zM50.5 172c2.5 1 2.5 1 7 6.5c6 6.2 12 11.8 18.8 17.1c5.7 4.5 11.3 9.1 16.7 13.9c1.3 1.2 2.7 2.3 4 3.5c.7 .7 1.3 1.3 2 2c-.2 .8-.3 1.7-.5 2.5c-6 2.9-11.7 4-18.5 4c-2.6-2.6-4.3-5.5-5.5-9c-.3 .1-.3 .1-1.5 .5c0 2.3 0 4.6 0 7c-.3 .5-.7 1-1 1.5c-13.3 0-24.6-4.6-34.5-13.5c-4.5-4.5-4.5-4.5-8.5-11c.5-2.5 .5-2.5 4.5-7c5-3 5-3 8-3.5c.5 .3 1 .7 1.5 1c2-.5 2-.5 4-2c.9-4.2 1.7-8.3 2.5-12.5c.4-.3 .6-.7 1-1z"/><path fill="#03B5BD" d="M58.5 149.5c2.5 .5 2.5 .5 7 3.5c9.8 5.2 18.9 7.9 30 8.5c2.2 .5 4.3 1 6.5 1.5c7 0 7 0 9-.5c2.3-.2 4.6-.3 7-.5c3.2-.9 6.3-1.9 9.5-3c1.3-.2 2.7-.3 4-.5c.2 .2 .3 .3 .5 .5c-.8 6.3-4.1 10.2-8 15c-.2 .5-.3 1-.5 1.5c1.7-1 3.3-2 5-3c.7 .2 1.3 .3 2 .5c-3 12.2-9.3 20.3-17.5 29.5c-.7 .7-.7 .7-4 4.5c-1 .8-2 1.7-3 2.5c-3.5-.5-3.5-.5-8-5c-2.3-1.8-4.7-3.7-7-5.5c-2.7-2.2-5.3-4.3-8-6.5c-.8-.6-.8-.6-4.5-3.5c-11-9.3-20.7-18.8-28.5-31c3-4 3-4 8.5-8.5z"/><ellipse cx="94" cy="94" rx="6.5" ry="6.5" fill="#212E34"/><ellipse cx="135" cy="85.6" rx="6.2" ry="5.8" fill="#212E34"/><g fill="#FAF9F8"><circle cx="91.5" cy="91.5" r="2.1"/><circle cx="134.6" cy="84.3" r="2"/></g><ellipse cx="124" cy="93.8" rx="3.4" ry="1.1" fill="#9B9D9E" transform="rotate(-6 124 93.8)"/></g></g><rect x="5" y="5" width="230" height="230" rx="45" fill="none" stroke="#5B6B73" stroke-width="10"/></symbol></defs>
|
|
2
2
|
<rect x="4" y="4" width="952" height="632" rx="18" fill="#151D22" stroke="#34434B" stroke-width="1.5"/>
|
|
3
3
|
<path d="M4 22a18 18 0 0 1 18-18h916a18 18 0 0 1 18 18v24H4z" fill="#212E34"/>
|
|
4
4
|
<circle cx="26" cy="25" r="5" fill="#9B9D9E" opacity="0.55"/>
|