@erclx/canon 5.26.0 → 5.28.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/README.md +2 -2
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/SKILL.md +2 -0
- package/claude/skills/auto-ship/SKILL.md +3 -11
- package/claude/skills/auto-ship/references/review-findings.md +20 -0
- package/claude/skills/backlog-triage/SKILL.md +2 -0
- package/claude/skills/bash-cli-script/SKILL.md +2 -0
- package/claude/skills/build-in-slices/SKILL.md +2 -0
- package/claude/skills/canon-cli/SKILL.md +2 -0
- package/claude/skills/canon-feedback/SKILL.md +2 -0
- package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
- package/claude/skills/canon-operator/SKILL.md +2 -0
- package/claude/skills/canon-rollout/SKILL.md +3 -11
- package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
- package/claude/skills/canvas/SKILL.md +13 -0
- package/claude/skills/ci-workflow/SKILL.md +2 -0
- package/claude/skills/code-craft/SKILL.md +2 -0
- package/claude/skills/codebase-layout/SKILL.md +2 -0
- package/claude/skills/context-fold/SKILL.md +3 -17
- package/claude/skills/context-fold/references/completion-report.md +26 -0
- package/claude/skills/create-rule/SKILL.md +2 -0
- package/claude/skills/create-skill/SKILL.md +2 -0
- package/claude/skills/create-standard/SKILL.md +2 -0
- package/claude/skills/decision-escalate/SKILL.md +2 -0
- package/claude/skills/deploy-app/SKILL.md +2 -0
- package/claude/skills/deprecation-migration/SKILL.md +2 -0
- package/claude/skills/design-extract/SKILL.md +2 -0
- package/claude/skills/design-taste/SKILL.md +2 -0
- package/claude/skills/docs-sync/SKILL.md +2 -0
- package/claude/skills/document-health/SKILL.md +2 -0
- package/claude/skills/draft-and-pick/SKILL.md +2 -0
- package/claude/skills/draft-doc/SKILL.md +2 -0
- package/claude/skills/draft-doc/references/wiki.md +1 -1
- package/claude/skills/draft-figure/SKILL.md +2 -0
- package/claude/skills/draft-identity/SKILL.md +2 -0
- package/claude/skills/draft-ready/SKILL.md +5 -1
- package/claude/skills/draft-screencast/SKILL.md +2 -0
- package/claude/skills/draft-slides/SKILL.md +2 -0
- package/claude/skills/git-branch/SKILL.md +2 -0
- package/claude/skills/git-commit/SKILL.md +2 -0
- package/claude/skills/git-followup/SKILL.md +2 -0
- package/claude/skills/git-issue/SKILL.md +2 -0
- package/claude/skills/git-pr/SKILL.md +2 -0
- package/claude/skills/git-pr/references/evidence.md +7 -7
- package/claude/skills/git-ship/SKILL.md +2 -0
- package/claude/skills/git-split/SKILL.md +2 -0
- package/claude/skills/git-stage/SKILL.md +2 -0
- package/claude/skills/git-worktree/SKILL.md +2 -0
- package/claude/skills/index-lookup/SKILL.md +2 -0
- package/claude/skills/markdown-propose/SKILL.md +2 -0
- package/claude/skills/memory-capture/SKILL.md +2 -0
- package/claude/skills/memory-review/SKILL.md +2 -0
- package/claude/skills/plan-feature/SKILL.md +2 -0
- package/claude/skills/plan-groundwork/SKILL.md +2 -0
- package/claude/skills/plan-intake/SKILL.md +2 -0
- package/claude/skills/plan-intake-answer/SKILL.md +2 -0
- package/claude/skills/project-commands/SKILL.md +2 -0
- package/claude/skills/read-frames/SKILL.md +2 -0
- package/claude/skills/record-screencast/SKILL.md +2 -0
- package/claude/skills/repo-metadata/SKILL.md +2 -0
- package/claude/skills/restate-plainly/SKILL.md +2 -0
- package/claude/skills/review-address/REQUIREMENT.md +4 -2
- package/claude/skills/review-address/SKILL.md +19 -14
- package/claude/skills/review-address/references/post-review-findings.md +16 -0
- package/claude/skills/review-address/references/rebase-conflicts.md +4 -2
- package/claude/skills/review-branch/SKILL.md +2 -0
- package/claude/skills/review-craft/SKILL.md +2 -0
- package/claude/skills/review-craft/references/ui.md +6 -6
- package/claude/skills/review-pr/SKILL.md +4 -8
- package/claude/skills/review-pr/references/body-variants.md +52 -0
- package/claude/skills/review-pr/references/key-changes.md +16 -0
- package/claude/skills/review-pr/references/post.md +15 -43
- package/claude/skills/review-ui/REQUIREMENT.md +2 -2
- package/claude/skills/review-ui/SKILL.md +16 -12
- package/claude/skills/review-ui/references/driving.md +8 -8
- package/claude/skills/review-ui/references/post.md +13 -3
- package/claude/skills/review-ui/references/tick.md +22 -0
- package/claude/skills/role-orchestrator/REQUIREMENT.md +15 -15
- package/claude/skills/role-orchestrator/SKILL.md +31 -42
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +18 -16
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +13 -3
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +6 -34
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +5 -5
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +12 -23
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +6 -6
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
- package/claude/skills/role-orchestrator/scripts/poll.ts +304 -10
- package/claude/skills/role-planner/REQUIREMENT.md +1 -1
- package/claude/skills/role-planner/SKILL.md +4 -2
- package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
- package/claude/skills/role-reviewer/SKILL.md +3 -1
- package/claude/skills/role-worker/SKILL.md +5 -2
- package/claude/skills/search-craft/SKILL.md +2 -0
- package/claude/skills/seed-sync/SKILL.md +2 -0
- package/claude/skills/session-compact/SKILL.md +2 -0
- package/claude/skills/session-map/SKILL.md +2 -0
- package/claude/skills/session-relay/SKILL.md +2 -0
- package/claude/skills/session-resume/SKILL.md +2 -0
- package/claude/skills/session-worktree/SKILL.md +3 -11
- package/claude/skills/session-worktree/references/install-command.md +20 -0
- package/claude/skills/sketch-design/SKILL.md +2 -0
- package/claude/skills/systematic-debugging/SKILL.md +2 -0
- package/claude/skills/target-check/SKILL.md +2 -0
- package/claude/skills/target-setup/SKILL.md +2 -0
- package/claude/skills/task-board/SKILL.md +2 -0
- package/claude/skills/teach-workspace/SKILL.md +5 -15
- package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
- package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
- package/claude/skills/teach-workspace/references/revisit.md +22 -0
- package/claude/skills/test-craft/SKILL.md +2 -0
- package/claude/skills/test-first/SKILL.md +2 -0
- package/claude/skills/ui-checklist/SKILL.md +3 -1
- package/claude/skills/ux-audit/SKILL.md +2 -0
- package/claude/skills/ux-measure/SKILL.md +2 -0
- package/claude/skills/ux-walkthrough/SKILL.md +2 -0
- package/claude/skills/video-craft/SKILL.md +2 -0
- package/claude/skills/write-human/SKILL.md +2 -0
- package/claude/skills/youtube-transcripts/SKILL.md +2 -0
- package/docs/agents/canvas.md +24 -8
- package/docs/agents/capture.md +5 -5
- package/docs/agents/commands.md +2 -2
- package/docs/agents/gate.md +3 -1
- package/docs/agents/index.md +5 -4
- package/docs/agents/install-and-sync.md +7 -0
- package/docs/agents/key-changes.md +1 -1
- package/docs/agents/pr-evidence.md +47 -23
- package/docs/agents/pr-local.md +19 -13
- package/docs/agents/pr-preview.md +44 -21
- package/docs/agents/pr-tick.md +72 -0
- package/docs/agents/sandbox.md +30 -6
- package/docs/agents/scripting.md +8 -1
- package/docs/agents/skills-audit.md +2 -1
- package/docs/agents/tasks-plans.md +1 -1
- package/docs/agents/tasks-validate.md +18 -18
- package/docs/agents/tasks.md +1 -1
- package/docs/workflow/operating-model.md +15 -21
- package/docs/workflow/parallel-features.md +24 -3
- package/docs/workflow/skill-map.md +3 -1
- package/docs/workflow/zshrc-aliases.md +1 -1
- package/package.json +1 -1
- package/scripts/core/regen-hero.ts +1 -1
- package/scripts/lib/sandbox-path.sh +6 -162
- package/src/audits/baseline.ts +1 -1
- package/src/audits/run.ts +2 -2
- package/src/canvas/address.ts +12 -3
- package/src/canvas/capture.ts +1 -1
- package/src/canvas/client/inspector/align-grid.tsx +162 -0
- package/src/canvas/client/inspector/color-field.tsx +201 -171
- package/src/canvas/client/inspector/color-inputs.tsx +139 -0
- package/src/canvas/client/inspector/color-picker.tsx +668 -0
- package/src/canvas/client/inspector/color-space.ts +240 -0
- package/src/canvas/client/inspector/field.tsx +66 -3
- package/src/canvas/client/inspector/fields.css +473 -6
- package/src/canvas/client/inspector/size-field.tsx +69 -0
- package/src/canvas/client/inspector/token-select.tsx +109 -0
- package/src/canvas/client/inspector/values.ts +117 -3
- package/src/canvas/client/inspector.tsx +311 -15
- package/src/canvas/client/keys.ts +54 -0
- package/src/canvas/client/selection.tsx +199 -0
- package/src/canvas/client/shell.css +210 -12
- package/src/canvas/client/state.ts +165 -14
- package/src/canvas/client/surface.tsx +341 -39
- package/src/canvas/client/tools.tsx +64 -0
- package/src/canvas/content.ts +51 -2
- package/src/canvas/edit.ts +49 -6
- package/src/canvas/editing.ts +203 -0
- package/src/canvas/global.d.ts +15 -0
- package/src/canvas/server.ts +149 -12
- package/src/canvas/tokens.ts +60 -18
- package/src/census/count.ts +1 -1
- package/src/claude/seeds.ts +3 -3
- package/src/claude/skills-audit.ts +28 -0
- package/src/claude/skills-drift.ts +1 -1
- package/src/claude/skills-families.ts +36 -0
- package/src/claude/skills-list.ts +76 -15
- package/src/claude/skills-reach.ts +1 -1
- package/src/commands/audits.ts +2 -2
- package/src/commands/canvas.ts +180 -3
- package/src/commands/claude.ts +70 -12
- package/src/commands/design.ts +145 -5
- package/src/commands/docs.ts +1 -1
- package/src/commands/feedback.ts +2 -2
- package/src/commands/gate.ts +9 -2
- package/src/commands/gov.ts +3 -3
- package/src/commands/hooks.ts +1 -1
- package/src/commands/indexes.ts +1 -1
- package/src/commands/init.ts +2 -2
- package/src/commands/intake.ts +1 -1
- package/src/commands/migrate.ts +2 -2
- package/src/commands/pr.ts +288 -65
- package/src/commands/records.ts +1 -1
- package/src/commands/repo.ts +1 -1
- package/src/commands/sandbox.ts +64 -15
- package/src/commands/sessions.ts +1 -1
- package/src/commands/slides.ts +1 -1
- package/src/commands/sync.ts +2 -2
- package/src/commands/tasks.ts +21 -4
- package/src/commands/teach.ts +1 -1
- package/src/commands/tooling.ts +1 -1
- package/src/commands/transcripts.ts +2 -2
- package/src/commands/upgrade.ts +1 -1
- package/src/commands/wiki.ts +1 -1
- package/src/commands/worktrees.ts +1 -1
- package/src/comments/scan.ts +1 -1
- package/src/comments/trend.ts +1 -1
- package/src/context/architecture.ts +1 -1
- package/src/context/citations.ts +3 -3
- package/src/context/classify/extract.ts +3 -3
- package/src/context/folders.ts +2 -2
- package/src/context/requirements.ts +1 -1
- package/src/design/project-fonts.ts +392 -0
- package/src/docs/read.ts +1 -1
- package/src/exec.ts +1 -1
- package/src/feedback/github.ts +2 -2
- package/src/gate/measures.ts +48 -10
- package/src/gate/sequencer.ts +56 -4
- package/src/gate/stages.ts +17 -0
- package/src/gate/suite-lock.ts +214 -0
- package/src/{git-files.ts → git/files.ts} +2 -2
- package/src/{git-ignore.ts → git/ignore.ts} +1 -1
- package/src/{worktree.ts → git/worktree.ts} +1 -1
- package/src/gov/citations.ts +1 -1
- package/src/gov/install.ts +1 -1
- package/src/gov/superseded.ts +1 -1
- package/src/gov/test-order.ts +2 -2
- package/src/help.ts +1 -1
- package/src/hooks/post-merge.ts +1 -1
- package/src/indexes/walk.ts +1 -1
- package/src/intake/folder.ts +1 -1
- package/src/labels/audit.ts +1 -1
- package/src/labels/map.ts +1 -1
- package/src/labels/phase.ts +3 -3
- package/src/markdown/files.ts +1 -1
- package/src/migrate/apply.ts +1 -1
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/records.ts +11 -11
- package/src/migrate/scratch-evidence.ts +1 -1
- package/src/migrate/surface-roots.ts +6 -6
- package/src/pr/bijection.ts +1 -1
- package/src/pr/evidence.ts +250 -59
- package/src/pr/local.ts +16 -10
- package/src/pr/preview.ts +78 -2
- package/src/pr/tick.ts +56 -0
- package/src/process/harness.ts +1 -1
- package/src/records/backup.ts +2 -2
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +1 -1
- package/src/records/size.ts +1 -1
- package/src/records/stale-canonical.ts +2 -2
- package/src/records/stale.ts +1 -1
- package/src/records/validate.ts +2 -2
- package/src/{project-root.ts → roots/project.ts} +1 -1
- package/src/{record-root.ts → roots/record.ts} +1 -1
- package/src/sandbox/coverage.ts +11 -12
- package/src/sandbox/equivalence.ts +57 -26
- package/src/sandbox/headless/dispatch.ts +264 -0
- package/src/sandbox/headless/run.ts +653 -0
- package/src/sandbox/headless/watch.ts +145 -0
- package/src/sandbox/hooks.ts +1 -1
- package/src/sandbox/provision.ts +90 -29
- package/src/sandbox/scenario.ts +588 -0
- package/src/sandbox/tree.ts +4 -8
- package/src/secrets/scan.ts +1 -1
- package/src/sessions/claim.ts +1 -1
- package/src/sessions/live.ts +4 -1
- package/src/sessions/resolve.ts +1 -1
- package/src/sessions/transfer/bundle.ts +2 -2
- package/src/slides/decks.ts +1 -1
- package/src/slides/package/fonts.ts +6 -2
- package/src/standards/audit.ts +2 -2
- package/src/standards/read.ts +1 -1
- package/src/sync/check.ts +2 -2
- package/src/sync/engine.ts +3 -3
- package/src/sync/git.ts +1 -1
- package/src/sync/history.ts +1 -1
- package/src/sync/layout.ts +2 -2
- package/src/sync/reverse.ts +1 -1
- package/src/sync/stamp.ts +1 -1
- package/src/sync/target.ts +1 -1
- package/src/sync/workflow.ts +1 -1
- package/src/targets/pulls.ts +2 -2
- package/src/targets/sweep.ts +2 -2
- package/src/{target.ts → targets/validate.ts} +1 -1
- package/src/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +1 -1
- package/src/tasks/label.ts +1 -1
- package/src/tasks/reach.ts +2 -2
- package/src/tasks/trunk.ts +1 -1
- package/src/tasks/validate.ts +38 -14
- package/src/teach/nav.ts +1 -1
- package/src/teach/workspace.ts +1 -1
- package/src/tooling/inject.ts +2 -2
- package/src/tooling/read.ts +1 -1
- package/src/tooling/scan.ts +1 -1
- package/src/tooling/subfolder.ts +1 -1
- package/src/version/installed.ts +1 -1
- package/src/version/skew.ts +1 -1
- package/src/web/readme-citations.ts +1 -1
- package/src/worktrees/bare-flag.ts +1 -1
- package/src/worktrees/reclaim.ts +2 -2
- package/src/worktrees/remove.ts +1 -1
- package/standards/board.md +3 -3
- package/standards/canvas.md +2 -1
- package/standards/plan.md +13 -7
- package/standards/skill.md +1 -0
- package/standards/wiki.md +12 -3
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
- package/tsconfig.json +1 -1
- package/scripts/lib/sandbox-dispatch.sh +0 -190
- /package/src/{client-commands.ts → gate/client-commands.ts} +0 -0
- /package/src/{git-base.ts → git/base.ts} +0 -0
- /package/src/{git-env.ts → git/env.ts} +0 -0
- /package/src/{legacy-path.ts → roots/legacy.ts} +0 -0
- /package/src/{surface-root.ts → roots/surface.ts} +0 -0
- /package/src/{copy.ts → sync/copy.ts} +0 -0
|
@@ -128,10 +128,164 @@
|
|
|
128
128
|
font-weight: 400;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/* An icon in a section's header lane or beside a field, square at row height. */
|
|
132
|
+
.section-icon {
|
|
133
|
+
flex: none;
|
|
134
|
+
display: grid;
|
|
135
|
+
place-items: center;
|
|
136
|
+
width: 24px;
|
|
137
|
+
height: 24px;
|
|
138
|
+
padding: 0;
|
|
139
|
+
border: 0;
|
|
140
|
+
border-radius: var(--radius-action);
|
|
141
|
+
background: transparent;
|
|
142
|
+
color: var(--color-muted);
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.section-icon:hover,
|
|
147
|
+
.section-icon[aria-pressed='true'] {
|
|
148
|
+
background: var(--color-surface);
|
|
149
|
+
color: var(--color-text);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.section-icon:disabled {
|
|
153
|
+
opacity: 0.5;
|
|
154
|
+
cursor: not-allowed;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.section-icon svg {
|
|
158
|
+
width: 14px;
|
|
159
|
+
height: 14px;
|
|
160
|
+
fill: none;
|
|
161
|
+
stroke: currentColor;
|
|
162
|
+
stroke-width: 1.5;
|
|
163
|
+
stroke-linecap: round;
|
|
164
|
+
stroke-linejoin: round;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* The alignment grid on the left, the direction and wrap stacked beside it. */
|
|
168
|
+
.flex-layout {
|
|
169
|
+
display: grid;
|
|
170
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
171
|
+
gap: var(--space-xs);
|
|
172
|
+
align-items: start;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.flex-layout-side {
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: var(--space-xs);
|
|
179
|
+
min-width: 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.flex-layout-side .glyph-field {
|
|
183
|
+
flex: 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.align-grid {
|
|
187
|
+
display: grid;
|
|
188
|
+
grid-template-columns: repeat(3, 1fr);
|
|
189
|
+
grid-template-rows: repeat(3, 24px);
|
|
190
|
+
padding: 2px;
|
|
191
|
+
border: 1px solid var(--color-border);
|
|
192
|
+
border-radius: var(--radius-action);
|
|
193
|
+
background: var(--color-surface);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.align-cell {
|
|
197
|
+
display: grid;
|
|
198
|
+
place-items: center;
|
|
199
|
+
min-width: 0;
|
|
200
|
+
padding: 0;
|
|
201
|
+
border: 0;
|
|
202
|
+
border-radius: var(--radius-action);
|
|
203
|
+
background: transparent;
|
|
204
|
+
color: var(--color-muted);
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.align-cell:hover {
|
|
209
|
+
color: var(--color-text);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.align-cell:disabled {
|
|
213
|
+
opacity: 0.5;
|
|
214
|
+
cursor: not-allowed;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.align-dot {
|
|
218
|
+
width: 3px;
|
|
219
|
+
height: 3px;
|
|
220
|
+
border-radius: 50%;
|
|
221
|
+
background: currentColor;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.align-cell[aria-checked='true'] {
|
|
225
|
+
color: var(--color-accent);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.align-cell[aria-checked='true'] .align-dot {
|
|
229
|
+
width: 10px;
|
|
230
|
+
height: 10px;
|
|
231
|
+
border-radius: 2px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
* A size's mode menu trails its number. The select is transparent over the
|
|
236
|
+
* chevron, so the platform's own list opens and the field keeps one height.
|
|
237
|
+
*/
|
|
238
|
+
.size-mode {
|
|
239
|
+
position: relative;
|
|
240
|
+
flex: none;
|
|
241
|
+
display: grid;
|
|
242
|
+
place-items: center;
|
|
243
|
+
width: 24px;
|
|
244
|
+
height: 22px;
|
|
245
|
+
border-radius: var(--radius-action);
|
|
246
|
+
color: var(--color-muted);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.size-mode:hover,
|
|
250
|
+
.size-mode:focus-within {
|
|
251
|
+
color: var(--color-text);
|
|
252
|
+
background: var(--color-chrome);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.size-mode-select {
|
|
256
|
+
position: absolute;
|
|
257
|
+
inset: 0;
|
|
258
|
+
width: 100%;
|
|
259
|
+
height: 100%;
|
|
260
|
+
opacity: 0;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.size-mode-select:disabled {
|
|
265
|
+
cursor: not-allowed;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.size-mode:has(.size-mode-select:disabled) {
|
|
269
|
+
opacity: 0.5;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* The select is invisible, so the project ring draws on the lane holding it. */
|
|
273
|
+
.size-mode:has(.size-mode-select:focus-visible) {
|
|
274
|
+
outline: 2px solid var(--color-accent);
|
|
275
|
+
outline-offset: 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.size-mode-chevron {
|
|
279
|
+
width: 12px;
|
|
280
|
+
height: 12px;
|
|
281
|
+
fill: none;
|
|
282
|
+
stroke: currentColor;
|
|
283
|
+
stroke-width: 1.5;
|
|
284
|
+
}
|
|
285
|
+
|
|
131
286
|
/*
|
|
132
287
|
* A color is one field: a swatch leading in the glyph's lane, the hex, and the
|
|
133
|
-
* opacity right-aligned
|
|
134
|
-
* reach past the panel's edge or scroll it sideways.
|
|
288
|
+
* opacity right-aligned, with the tokens and the eyedropper trailing the row.
|
|
135
289
|
*/
|
|
136
290
|
.color-field {
|
|
137
291
|
min-width: 0;
|
|
@@ -246,15 +400,218 @@
|
|
|
246
400
|
user-select: none;
|
|
247
401
|
}
|
|
248
402
|
|
|
249
|
-
|
|
403
|
+
/*
|
|
404
|
+
* The picker and the token list float beside the panel, in the top layer
|
|
405
|
+
* where the browser has one. The panel scrolls and clips, so neither sits in
|
|
406
|
+
* its flow. The script places each at fixed coordinates inside the window, and
|
|
407
|
+
* the caps below hold a window too small for the whole picker.
|
|
408
|
+
*/
|
|
409
|
+
.color-popover {
|
|
410
|
+
position: fixed;
|
|
411
|
+
inset: auto;
|
|
412
|
+
z-index: 10;
|
|
413
|
+
max-width: calc(100vw - 16px);
|
|
414
|
+
max-height: calc(100vh - 16px);
|
|
415
|
+
margin: 0;
|
|
416
|
+
padding: var(--space-sm);
|
|
417
|
+
overflow: auto;
|
|
418
|
+
border: 1px solid var(--color-border);
|
|
419
|
+
border-radius: var(--radius-panel);
|
|
420
|
+
background: var(--color-chrome);
|
|
421
|
+
color: var(--color-text);
|
|
422
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / 0.35);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.color-popover.is-tokens {
|
|
426
|
+
width: 14rem;
|
|
427
|
+
padding: var(--space-xs);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.picker {
|
|
431
|
+
--picker-hue: red;
|
|
432
|
+
--picker-solid: transparent;
|
|
433
|
+
display: grid;
|
|
434
|
+
grid-template-columns: 14rem 14px 14px 13rem;
|
|
435
|
+
gap: var(--space-sm);
|
|
436
|
+
align-items: stretch;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
* The area draws white to the hue left to right, then black over it bottom
|
|
441
|
+
* to top, so a point reads as saturation across and brightness up.
|
|
442
|
+
*/
|
|
443
|
+
.picker-area {
|
|
444
|
+
position: relative;
|
|
445
|
+
aspect-ratio: 1;
|
|
446
|
+
border-radius: var(--radius-action);
|
|
447
|
+
background:
|
|
448
|
+
linear-gradient(to top, #000, transparent),
|
|
449
|
+
linear-gradient(to right, #fff, var(--picker-hue));
|
|
450
|
+
cursor: crosshair;
|
|
451
|
+
touch-action: none;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.picker-thumb {
|
|
455
|
+
position: absolute;
|
|
456
|
+
width: 12px;
|
|
457
|
+
height: 12px;
|
|
458
|
+
border: 2px solid #fff;
|
|
459
|
+
border-radius: 50%;
|
|
460
|
+
box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
|
|
461
|
+
transform: translate(-50%, -50%);
|
|
462
|
+
pointer-events: none;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.picker-slider {
|
|
466
|
+
appearance: none;
|
|
467
|
+
width: 14px;
|
|
468
|
+
height: 100%;
|
|
469
|
+
margin: 0;
|
|
470
|
+
border-radius: var(--radius-action);
|
|
471
|
+
writing-mode: vertical-lr;
|
|
472
|
+
cursor: pointer;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* Hue runs red at the top through the wheel and back to red at the foot. */
|
|
476
|
+
.picker-hue {
|
|
477
|
+
background: linear-gradient(
|
|
478
|
+
to bottom,
|
|
479
|
+
#f00,
|
|
480
|
+
#ff0 17%,
|
|
481
|
+
#0f0 33%,
|
|
482
|
+
#0ff 50%,
|
|
483
|
+
#00f 67%,
|
|
484
|
+
#f0f 83%,
|
|
485
|
+
#f00
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* Alpha runs opaque at the top to clear at the foot, over a checker. */
|
|
490
|
+
.picker-alpha {
|
|
491
|
+
direction: rtl;
|
|
492
|
+
background-image:
|
|
493
|
+
linear-gradient(to bottom, var(--picker-solid), transparent),
|
|
494
|
+
linear-gradient(45deg, var(--color-border) 25%, transparent 25%),
|
|
495
|
+
linear-gradient(-45deg, var(--color-border) 25%, transparent 25%),
|
|
496
|
+
linear-gradient(45deg, transparent 75%, var(--color-border) 75%),
|
|
497
|
+
linear-gradient(-45deg, transparent 75%, var(--color-border) 75%);
|
|
498
|
+
background-size:
|
|
499
|
+
auto,
|
|
500
|
+
8px 8px,
|
|
501
|
+
8px 8px,
|
|
502
|
+
8px 8px,
|
|
503
|
+
8px 8px;
|
|
504
|
+
background-position:
|
|
505
|
+
0 0,
|
|
506
|
+
0 0,
|
|
507
|
+
0 4px,
|
|
508
|
+
4px -4px,
|
|
509
|
+
-4px 0;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.picker-slider::-webkit-slider-thumb {
|
|
513
|
+
appearance: none;
|
|
514
|
+
width: 14px;
|
|
515
|
+
height: 6px;
|
|
516
|
+
border: 2px solid #fff;
|
|
517
|
+
border-radius: 2px;
|
|
518
|
+
box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.picker-slider::-moz-range-thumb {
|
|
522
|
+
width: 10px;
|
|
523
|
+
height: 2px;
|
|
524
|
+
border: 2px solid #fff;
|
|
525
|
+
border-radius: 2px;
|
|
526
|
+
background: transparent;
|
|
527
|
+
box-shadow: 0 0 0 1px rgb(0 0 0 / 0.5);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.picker-side {
|
|
250
531
|
display: flex;
|
|
251
532
|
flex-direction: column;
|
|
252
533
|
gap: var(--space-xs);
|
|
253
|
-
|
|
254
|
-
|
|
534
|
+
min-width: 0;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.picker-compare {
|
|
538
|
+
display: grid;
|
|
539
|
+
grid-template-columns: 1fr 1fr;
|
|
540
|
+
height: 3.5rem;
|
|
541
|
+
overflow: hidden;
|
|
255
542
|
border: 1px solid var(--color-border);
|
|
256
543
|
border-radius: var(--radius-action);
|
|
257
|
-
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/* Each half paints over the checker, so an alpha below full reads as one. */
|
|
547
|
+
.picker-previous,
|
|
548
|
+
.picker-new {
|
|
549
|
+
--paint: transparent;
|
|
550
|
+
padding: 0;
|
|
551
|
+
border: 0;
|
|
552
|
+
background-image:
|
|
553
|
+
linear-gradient(var(--paint), var(--paint)),
|
|
554
|
+
linear-gradient(45deg, var(--color-border) 25%, transparent 25%),
|
|
555
|
+
linear-gradient(-45deg, var(--color-border) 25%, transparent 25%),
|
|
556
|
+
linear-gradient(45deg, transparent 75%, var(--color-border) 75%),
|
|
557
|
+
linear-gradient(-45deg, transparent 75%, var(--color-border) 75%);
|
|
558
|
+
background-size:
|
|
559
|
+
auto,
|
|
560
|
+
8px 8px,
|
|
561
|
+
8px 8px,
|
|
562
|
+
8px 8px,
|
|
563
|
+
8px 8px;
|
|
564
|
+
background-position:
|
|
565
|
+
0 0,
|
|
566
|
+
0 0,
|
|
567
|
+
0 4px,
|
|
568
|
+
4px -4px,
|
|
569
|
+
-4px 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.picker-previous {
|
|
573
|
+
cursor: pointer;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.picker-compare-labels {
|
|
577
|
+
display: grid;
|
|
578
|
+
grid-template-columns: 1fr 1fr;
|
|
579
|
+
color: var(--color-text-secondary);
|
|
580
|
+
text-align: center;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.picker-row {
|
|
584
|
+
display: grid;
|
|
585
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
586
|
+
gap: 1px;
|
|
587
|
+
overflow: hidden;
|
|
588
|
+
border-radius: var(--radius-action);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.picker-channel {
|
|
592
|
+
display: flex;
|
|
593
|
+
flex-direction: column;
|
|
594
|
+
align-items: stretch;
|
|
595
|
+
min-width: 0;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.picker-number {
|
|
599
|
+
min-width: 0;
|
|
600
|
+
min-height: 24px;
|
|
601
|
+
padding: 2px var(--space-xs);
|
|
602
|
+
border: 0;
|
|
603
|
+
background: var(--color-surface);
|
|
604
|
+
color: var(--color-text);
|
|
605
|
+
font: inherit;
|
|
606
|
+
font-variant-numeric: tabular-nums;
|
|
607
|
+
text-align: center;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.picker-letter {
|
|
611
|
+
color: var(--color-muted);
|
|
612
|
+
font-size: 0.75em;
|
|
613
|
+
text-align: center;
|
|
614
|
+
user-select: none;
|
|
258
615
|
}
|
|
259
616
|
|
|
260
617
|
.token-list {
|
|
@@ -290,3 +647,113 @@
|
|
|
290
647
|
text-overflow: ellipsis;
|
|
291
648
|
white-space: nowrap;
|
|
292
649
|
}
|
|
650
|
+
|
|
651
|
+
/* A trailing icon on the color row, square at the row's height. */
|
|
652
|
+
.color-icon {
|
|
653
|
+
flex: none;
|
|
654
|
+
display: grid;
|
|
655
|
+
place-items: center;
|
|
656
|
+
width: 24px;
|
|
657
|
+
height: 24px;
|
|
658
|
+
padding: 0;
|
|
659
|
+
border: 0;
|
|
660
|
+
border-radius: var(--radius-action);
|
|
661
|
+
background: transparent;
|
|
662
|
+
color: var(--color-muted);
|
|
663
|
+
cursor: pointer;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.color-icon:hover,
|
|
667
|
+
.color-icon[aria-expanded='true'] {
|
|
668
|
+
background: var(--color-surface);
|
|
669
|
+
color: var(--color-text);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.color-icon:disabled {
|
|
673
|
+
opacity: 0.5;
|
|
674
|
+
cursor: not-allowed;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.color-icon svg {
|
|
678
|
+
width: 14px;
|
|
679
|
+
height: 14px;
|
|
680
|
+
fill: currentColor;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/*
|
|
684
|
+
* A value picked from a fixed list. The native select draws its value as the
|
|
685
|
+
* field's text and the chevron sits over its right edge, so the platform list
|
|
686
|
+
* opens on press and the field keeps the height of every other one.
|
|
687
|
+
*/
|
|
688
|
+
.glyph-field.is-select {
|
|
689
|
+
position: relative;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.glyph-select {
|
|
693
|
+
appearance: none;
|
|
694
|
+
padding-right: 22px;
|
|
695
|
+
cursor: pointer;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.select-chevron {
|
|
699
|
+
position: absolute;
|
|
700
|
+
right: 6px;
|
|
701
|
+
width: 12px;
|
|
702
|
+
height: 12px;
|
|
703
|
+
fill: none;
|
|
704
|
+
stroke: var(--color-muted);
|
|
705
|
+
stroke-width: 1.5;
|
|
706
|
+
pointer-events: none;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/* A raw marker inside a field sits before its menu, clear of the value. */
|
|
710
|
+
.glyph-field .raw {
|
|
711
|
+
padding: 0 2px 0 var(--space-xs);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* Mutually exclusive choices for one value, drawn as one field split evenly. */
|
|
715
|
+
.segmented {
|
|
716
|
+
display: grid;
|
|
717
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
718
|
+
gap: 2px;
|
|
719
|
+
min-height: 24px;
|
|
720
|
+
padding: 1px;
|
|
721
|
+
border: 1px solid var(--color-border);
|
|
722
|
+
border-radius: var(--radius-action);
|
|
723
|
+
background: var(--color-surface);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.segment {
|
|
727
|
+
display: grid;
|
|
728
|
+
place-items: center;
|
|
729
|
+
min-height: 22px;
|
|
730
|
+
padding: 0;
|
|
731
|
+
border: 0;
|
|
732
|
+
border-radius: var(--radius-action);
|
|
733
|
+
background: transparent;
|
|
734
|
+
color: var(--color-muted);
|
|
735
|
+
cursor: pointer;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.segment:hover {
|
|
739
|
+
color: var(--color-text);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.segment[aria-pressed='true'] {
|
|
743
|
+
background: var(--color-chrome);
|
|
744
|
+
color: var(--color-text);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.segment:disabled {
|
|
748
|
+
opacity: 0.5;
|
|
749
|
+
cursor: not-allowed;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.segment svg {
|
|
753
|
+
width: 14px;
|
|
754
|
+
height: 14px;
|
|
755
|
+
fill: none;
|
|
756
|
+
stroke: currentColor;
|
|
757
|
+
stroke-width: 1.5;
|
|
758
|
+
stroke-linecap: round;
|
|
759
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from 'preact'
|
|
3
|
+
import { Field, type Scrub } from '@/canvas/client/inspector/field'
|
|
4
|
+
|
|
5
|
+
/** The modes a size takes besides a number, by the name the field shows. */
|
|
6
|
+
const MODES = ['Fill', 'Fit'] as const
|
|
7
|
+
|
|
8
|
+
interface SizeFieldProps {
|
|
9
|
+
readonly label: string
|
|
10
|
+
readonly glyph: string
|
|
11
|
+
/** The value as the panel shows it, a mode name or a number. */
|
|
12
|
+
readonly initial: string
|
|
13
|
+
readonly isBusy: boolean
|
|
14
|
+
/** Takes a typed number or a picked mode name, each as shown. */
|
|
15
|
+
readonly onCommit: (value: string) => void
|
|
16
|
+
readonly scrub?: Scrub
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A size that takes a number or a mode. The chevron is a native select, so
|
|
21
|
+
* its list opens above the panel's clipping and answers the keyboard as the
|
|
22
|
+
* platform does, and typing a number replaces the mode.
|
|
23
|
+
*/
|
|
24
|
+
export function SizeField({
|
|
25
|
+
label,
|
|
26
|
+
glyph,
|
|
27
|
+
initial,
|
|
28
|
+
isBusy,
|
|
29
|
+
onCommit,
|
|
30
|
+
scrub,
|
|
31
|
+
}: SizeFieldProps): JSX.Element {
|
|
32
|
+
const mode = (MODES as readonly string[]).includes(initial) ? initial : ''
|
|
33
|
+
return (
|
|
34
|
+
<Field
|
|
35
|
+
label={label}
|
|
36
|
+
glyph={glyph}
|
|
37
|
+
initial={initial}
|
|
38
|
+
isBusy={isBusy}
|
|
39
|
+
onCommit={onCommit}
|
|
40
|
+
scrub={scrub}
|
|
41
|
+
trailing={
|
|
42
|
+
<span class="size-mode">
|
|
43
|
+
<select
|
|
44
|
+
class="size-mode-select"
|
|
45
|
+
aria-label={`${label} mode`}
|
|
46
|
+
value={mode}
|
|
47
|
+
disabled={isBusy}
|
|
48
|
+
onChange={(event) => {
|
|
49
|
+
const picked = event.currentTarget.value
|
|
50
|
+
if (picked && picked !== mode) onCommit(picked)
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<option value="" disabled hidden>
|
|
54
|
+
Fixed
|
|
55
|
+
</option>
|
|
56
|
+
{MODES.map((name) => (
|
|
57
|
+
<option key={name} value={name}>
|
|
58
|
+
{name}
|
|
59
|
+
</option>
|
|
60
|
+
))}
|
|
61
|
+
</select>
|
|
62
|
+
<svg class="size-mode-chevron" viewBox="0 0 16 16" aria-hidden="true">
|
|
63
|
+
<path d="M4.5 6.5 8 10l3.5-3.5" />
|
|
64
|
+
</svg>
|
|
65
|
+
</span>
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from 'preact'
|
|
3
|
+
import { Field, type Scrub } from '@/canvas/client/inspector/field'
|
|
4
|
+
|
|
5
|
+
/** The menu entry that hands the value back to the field for typing. */
|
|
6
|
+
const RAW = ''
|
|
7
|
+
|
|
8
|
+
interface TokenSelectProps {
|
|
9
|
+
readonly label: string
|
|
10
|
+
readonly glyph: string
|
|
11
|
+
/** The value as the panel shows it, a token's name where one stands. */
|
|
12
|
+
readonly initial: string
|
|
13
|
+
/** One token group's names, in the order the token sheet sorts them. */
|
|
14
|
+
readonly tokens: readonly string[]
|
|
15
|
+
/** The token the element names, where it names one. */
|
|
16
|
+
readonly token?: string
|
|
17
|
+
readonly isRaw: boolean
|
|
18
|
+
readonly isBusy: boolean
|
|
19
|
+
readonly isWide?: boolean
|
|
20
|
+
readonly placeholder?: string
|
|
21
|
+
/** Takes a typed value as typed. */
|
|
22
|
+
readonly onCommit: (value: string) => void
|
|
23
|
+
/** Takes the name of a token picked from the menu or typed whole. */
|
|
24
|
+
readonly onPick: (name: string) => void
|
|
25
|
+
readonly scrub?: Scrub
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A value that takes a token or a typed literal. The menu lists the group's
|
|
30
|
+
* tokens first and a raw value last, which returns focus to the field, so a
|
|
31
|
+
* token is one pick away and a literal is still one keystroke. A group with no
|
|
32
|
+
* tokens leaves the field alone, with no menu and no raw marker.
|
|
33
|
+
*/
|
|
34
|
+
export function TokenSelect({
|
|
35
|
+
label,
|
|
36
|
+
glyph,
|
|
37
|
+
initial,
|
|
38
|
+
tokens,
|
|
39
|
+
token,
|
|
40
|
+
isRaw,
|
|
41
|
+
isBusy,
|
|
42
|
+
isWide,
|
|
43
|
+
placeholder,
|
|
44
|
+
onCommit,
|
|
45
|
+
onPick,
|
|
46
|
+
scrub,
|
|
47
|
+
}: TokenSelectProps): JSX.Element {
|
|
48
|
+
const hasTokens = tokens.length > 0
|
|
49
|
+
return (
|
|
50
|
+
<Field
|
|
51
|
+
label={label}
|
|
52
|
+
glyph={glyph}
|
|
53
|
+
initial={initial}
|
|
54
|
+
isBusy={isBusy}
|
|
55
|
+
isWide={isWide}
|
|
56
|
+
placeholder={placeholder}
|
|
57
|
+
onCommit={(typed) => {
|
|
58
|
+
if (tokens.includes(typed)) onPick(typed)
|
|
59
|
+
else onCommit(typed)
|
|
60
|
+
}}
|
|
61
|
+
scrub={scrub}
|
|
62
|
+
trailing={
|
|
63
|
+
hasTokens ? (
|
|
64
|
+
<>
|
|
65
|
+
{isRaw ? (
|
|
66
|
+
<span
|
|
67
|
+
class="raw"
|
|
68
|
+
title="Set as a raw value, so it will not follow the theme. Pick a token to fix it"
|
|
69
|
+
>
|
|
70
|
+
raw
|
|
71
|
+
</span>
|
|
72
|
+
) : null}
|
|
73
|
+
<span class="size-mode">
|
|
74
|
+
<select
|
|
75
|
+
class="size-mode-select"
|
|
76
|
+
aria-label={`${label} tokens`}
|
|
77
|
+
value={token ?? RAW}
|
|
78
|
+
disabled={isBusy}
|
|
79
|
+
onChange={(event) => {
|
|
80
|
+
const picked = event.currentTarget.value
|
|
81
|
+
if (picked === RAW) {
|
|
82
|
+
event.currentTarget
|
|
83
|
+
.closest('.glyph-field')
|
|
84
|
+
?.querySelector<HTMLInputElement>('.glyph-field-input')
|
|
85
|
+
?.focus()
|
|
86
|
+
} else if (picked !== token) onPick(picked)
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
{tokens.map((name) => (
|
|
90
|
+
<option key={name} value={name}>
|
|
91
|
+
{name}
|
|
92
|
+
</option>
|
|
93
|
+
))}
|
|
94
|
+
<option value={RAW}>Raw value</option>
|
|
95
|
+
</select>
|
|
96
|
+
<svg
|
|
97
|
+
class="size-mode-chevron"
|
|
98
|
+
viewBox="0 0 16 16"
|
|
99
|
+
aria-hidden="true"
|
|
100
|
+
>
|
|
101
|
+
<path d="M4.5 6.5 8 10l3.5-3.5" />
|
|
102
|
+
</svg>
|
|
103
|
+
</span>
|
|
104
|
+
</>
|
|
105
|
+
) : null
|
|
106
|
+
}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
}
|