@erclx/canon 5.25.0 → 5.27.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 +4 -3
- 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 +14 -1
- 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/REQUIREMENT.md +6 -2
- package/claude/skills/context-fold/SKILL.md +9 -21
- package/claude/skills/context-fold/references/anchor-sweep.md +2 -2
- 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/REQUIREMENT.md +1 -1
- package/claude/skills/design-taste/SKILL.md +6 -2
- package/claude/skills/design-taste/references/adopted.md +40 -0
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/systems.md +1 -7
- package/claude/skills/design-taste/references/tells.md +2 -24
- package/claude/skills/docs-sync/REQUIREMENT.md +2 -1
- package/claude/skills/docs-sync/SKILL.md +2 -0
- package/claude/skills/document-health/REQUIREMENT.md +2 -0
- package/claude/skills/document-health/SKILL.md +23 -0
- package/claude/skills/document-health/references/axes.md +12 -0
- package/claude/skills/draft-and-pick/SKILL.md +3 -1
- 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 +3 -1
- package/claude/skills/draft-ready/SKILL.md +2 -0
- package/claude/skills/draft-screencast/SKILL.md +11 -7
- package/claude/skills/draft-slides/REQUIREMENT.md +17 -12
- package/claude/skills/draft-slides/SKILL.md +33 -20
- 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-issue/references/issue.md +10 -0
- package/claude/skills/git-pr/SKILL.md +2 -0
- package/claude/skills/git-pr/references/evidence.md +14 -6
- package/claude/skills/git-pr/references/labels.md +12 -0
- package/claude/skills/git-ship/SKILL.md +2 -0
- package/claude/skills/git-split/SKILL.md +21 -8
- 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/markdown-propose/references/format.md +9 -0
- package/claude/skills/memory-capture/SKILL.md +2 -0
- package/claude/skills/memory-review/SKILL.md +2 -0
- package/claude/skills/memory-review/references/later-phases.md +7 -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/SKILL.md +3 -7
- package/claude/skills/review-address/references/post-review-findings.md +16 -0
- 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 +2 -2
- 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/SKILL.md +3 -1
- package/claude/skills/review-ui/references/post.md +4 -2
- package/claude/skills/role-orchestrator/SKILL.md +5 -14
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +8 -0
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +13 -0
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -33
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -21
- 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-watch.md +26 -0
- package/claude/skills/role-planner/SKILL.md +2 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
- package/claude/skills/role-reviewer/SKILL.md +2 -0
- package/claude/skills/role-worker/SKILL.md +2 -0
- package/claude/skills/search-craft/REQUIREMENT.md +1 -1
- package/claude/skills/search-craft/SKILL.md +5 -1
- package/claude/skills/search-craft/references/adopted.md +30 -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/target-setup/references/detect.md +8 -0
- package/claude/skills/target-setup/references/indexes.md +13 -0
- package/claude/skills/target-setup/references/verify.md +8 -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 +4 -0
- package/claude/skills/test-first/references/adopted.md +36 -0
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- 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 +3 -1
- package/claude/skills/write-human/references/machine-tells.md +13 -0
- package/claude/skills/youtube-transcripts/SKILL.md +2 -0
- package/docs/agents/canvas.md +24 -6
- package/docs/agents/capture.md +6 -6
- package/docs/agents/commands.md +5 -5
- package/docs/agents/index.md +5 -5
- package/docs/agents/install-and-sync.md +7 -0
- package/docs/agents/key-changes.md +1 -1
- package/docs/agents/pr-evidence.md +38 -15
- package/docs/agents/pr-local.md +19 -13
- package/docs/agents/pr-preview.md +44 -21
- package/docs/agents/records-stale.md +22 -9
- package/docs/agents/records.md +1 -1
- package/docs/agents/sandbox.md +29 -5
- package/docs/agents/scripting.md +8 -1
- package/docs/agents/skills-audit.md +11 -3
- package/docs/agents/slides.md +15 -3
- package/docs/workflow/ai-workflow.md +1 -1
- package/docs/workflow/index.md +1 -0
- package/docs/workflow/operating-model.md +5 -6
- package/docs/workflow/parallel-features.md +23 -2
- package/docs/workflow/skill-map-on-request.md +42 -0
- package/docs/workflow/skill-map.md +3 -34
- 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-git.sh +1 -9
- package/scripts/lib/sandbox-path.sh +6 -162
- package/scripts/sandbox-hook.sh +99 -0
- package/src/audits/run.ts +1 -1
- package/src/canvas/address.ts +12 -3
- package/src/canvas/capture.ts +1 -9
- package/src/canvas/client/inspector/align-grid.tsx +162 -0
- package/src/canvas/client/inspector/color-field.tsx +398 -0
- 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 +5 -1
- package/src/canvas/client/inspector/fields.css +547 -17
- package/src/canvas/client/inspector/size-field.tsx +69 -0
- package/src/canvas/client/inspector/values.ts +166 -1
- package/src/canvas/client/inspector.tsx +236 -73
- package/src/canvas/client/selection.tsx +197 -0
- package/src/canvas/client/shell.css +104 -12
- package/src/canvas/client/state.ts +69 -9
- package/src/canvas/client/surface.tsx +147 -22
- package/src/canvas/content.ts +50 -1
- package/src/canvas/edit.ts +37 -4
- 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 +59 -17
- package/src/capture/render.ts +45 -7
- package/src/capture/sources.ts +43 -0
- package/src/census/count.ts +2 -2
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/seeds.ts +2 -2
- package/src/claude/skills-audit.ts +96 -14
- 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/commands/audits.ts +2 -2
- package/src/commands/canvas.ts +179 -2
- package/src/commands/claude.ts +94 -11
- package/src/commands/design.ts +142 -2
- package/src/commands/feedback.ts +3 -3
- package/src/commands/gov.ts +1 -1
- package/src/commands/hooks.ts +1 -1
- package/src/commands/indexes.ts +1 -1
- package/src/commands/init.ts +1 -1
- package/src/commands/intake.ts +1 -1
- package/src/commands/migrate.ts +1 -1
- package/src/commands/pr.ts +105 -58
- package/src/commands/records.ts +89 -7
- package/src/commands/repo.ts +1 -1
- package/src/commands/sandbox.ts +103 -11
- package/src/commands/sessions.ts +1 -1
- package/src/commands/slides.ts +31 -77
- package/src/commands/sync.ts +1 -1
- package/src/commands/tasks.ts +2 -2
- package/src/commands/teach.ts +1 -1
- package/src/commands/transcripts.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/audit.ts +1 -1
- package/src/context/citations.ts +1 -1
- package/src/context/classify/extract.ts +2 -2
- package/src/design/project-fonts.ts +392 -0
- package/src/design/tokens.ts +2 -18
- package/src/docs/read.ts +1 -1
- package/src/{github.ts → feedback/github.ts} +2 -2
- package/src/{client-commands.ts → gate/client-commands.ts} +1 -1
- package/src/gate/measures.ts +53 -13
- package/src/gate/sequencer.ts +1 -1
- package/src/gate/stages.ts +9 -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 +2 -2
- package/src/gov/install.ts +1 -1
- package/src/gov/payload.ts +1 -1
- package/src/gov/superseded.ts +3 -3
- 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/labels/audit.ts +1 -1
- package/src/markdown/ceiling.ts +1 -1
- package/src/markdown/files.ts +1 -1
- package/src/migrate/apply.ts +1 -1
- package/src/pr/bijection.ts +1 -1
- package/src/pr/evidence.ts +96 -43
- package/src/pr/local.ts +16 -10
- package/src/pr/preview.ts +78 -2
- package/src/record-root.ts +1 -0
- package/src/records/backup.ts +2 -1
- package/src/records/push-guard.ts +1 -1
- package/src/records/stale-canonical.ts +146 -0
- package/src/records/stale.ts +1 -1
- 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 +128 -0
- package/src/sandbox/provision.ts +607 -0
- package/src/sandbox/tree.ts +99 -4
- package/src/secrets/marker.ts +2 -2
- package/src/secrets/scan.ts +2 -2
- package/src/sessions/claim.ts +1 -1
- package/src/sessions/resolve.ts +1 -1
- package/src/sessions/transfer/bundle.ts +1 -1
- package/src/shipped/references.ts +2 -2
- package/src/slides/convert/export.ts +2 -16
- package/src/slides/decks.ts +110 -0
- 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 +2 -2
- package/src/sync/git.ts +1 -1
- package/src/sync/history.ts +1 -1
- package/src/sync/reverse.ts +1 -1
- package/src/sync/seeds-report.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/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +1 -1
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/trunk.ts +1 -1
- package/src/teach/nav.ts +1 -1
- package/src/tooling/inject.ts +2 -2
- package/src/tooling/subfolder.ts +1 -1
- package/src/web/readme-citations.ts +2 -2
- package/src/worktrees/bare-flag.ts +1 -1
- package/src/worktrees/reclaim.ts +2 -2
- package/src/worktrees/remove.ts +1 -1
- package/standards/architecture.md +1 -0
- package/standards/canvas.md +3 -2
- package/standards/requirements.md +2 -0
- package/standards/skill-practice.md +2 -1
- package/standards/skill.md +2 -0
- package/standards/wiki.md +12 -3
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
- package/scripts/lib/sandbox-dispatch.sh +0 -190
- package/scripts/manage-sandbox.sh +0 -472
- package/src/slides/layouts.ts +0 -478
- package/src/slides/parse.ts +0 -84
- package/src/slides/render.ts +0 -110
- package/src/slides/styles.ts +0 -96
- /package/claude/skills/write-human/references/{source-material.md → adopted.md} +0 -0
- /package/src/{commands/feedback-format.ts → feedback/body.ts} +0 -0
- /package/src/{github-format.ts → feedback/github-format.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/{frontmatter.ts → markdown/frontmatter.ts} +0 -0
- /package/src/{binary.ts → sweep/binary.ts} +0 -0
- /package/src/{exempt-marker.ts → sweep/exempt-marker.ts} +0 -0
- /package/src/{copy.ts → sync/copy.ts} +0 -0
- /package/src/{target.ts → targets/validate.ts} +0 -0
- /package/src/{paths.ts → tasks/paths.ts} +0 -0
- /package/src/{seed-marker.ts → tooling/seed-marker.ts} +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversions between the hex the writer takes and the spaces the picker
|
|
3
|
+
* edits in. Channels are 0 to 255, hues are degrees, and every other component
|
|
4
|
+
* is 0 to 100. LCH is CIE LCH as CSS `lch()` defines it, and the matrices are
|
|
5
|
+
* the ones CSS Color 4 publishes in its sample code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface Rgb {
|
|
9
|
+
readonly r: number
|
|
10
|
+
readonly g: number
|
|
11
|
+
readonly b: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Hsv {
|
|
15
|
+
readonly h: number
|
|
16
|
+
readonly s: number
|
|
17
|
+
readonly v: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Hsl {
|
|
21
|
+
readonly h: number
|
|
22
|
+
readonly s: number
|
|
23
|
+
readonly l: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Lch {
|
|
27
|
+
readonly l: number
|
|
28
|
+
readonly c: number
|
|
29
|
+
readonly h: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type Vector = readonly [number, number, number]
|
|
33
|
+
type Matrix = readonly [Vector, Vector, Vector]
|
|
34
|
+
|
|
35
|
+
const LINEAR_SRGB_TO_XYZ: Matrix = [
|
|
36
|
+
[506752 / 1228815, 87881 / 245763, 12673 / 70218],
|
|
37
|
+
[87098 / 409605, 175762 / 245763, 12673 / 175545],
|
|
38
|
+
[7918 / 409605, 87881 / 737289, 1001167 / 1053270],
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const XYZ_TO_LINEAR_SRGB: Matrix = [
|
|
42
|
+
[12831 / 3959, -329 / 214, -1974 / 3959],
|
|
43
|
+
[-851781 / 878810, 1648619 / 878810, 36519 / 878810],
|
|
44
|
+
[705 / 12673, -2585 / 12673, 705 / 667],
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
/** Bradford chromatic adaptation, since sRGB is D65 and `lch()` is D50. */
|
|
48
|
+
const D65_TO_D50: Matrix = [
|
|
49
|
+
[1.0479297925449969, 0.022946870601609652, -0.05019226628920524],
|
|
50
|
+
[0.02962780877005599, 0.9904344267538799, -0.017073799063418826],
|
|
51
|
+
[-0.009243040646204504, 0.015055191490298152, 0.7518742814281371],
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const D50_TO_D65: Matrix = [
|
|
55
|
+
[0.955473421488075, -0.02309845494876471, 0.06325924320057072],
|
|
56
|
+
[-0.0283697093338637, 1.0099953980813041, 0.021041441191917323],
|
|
57
|
+
[0.012314014864481998, -0.020507649298898964, 1.330365926242124],
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const D50_WHITE: Vector = [0.3457 / 0.3585, 1, (1 - 0.3457 - 0.3585) / 0.3585]
|
|
61
|
+
|
|
62
|
+
const EPSILON = 216 / 24389
|
|
63
|
+
const KAPPA = 24389 / 27
|
|
64
|
+
|
|
65
|
+
/** Chroma below this reads as no hue, which rounding noise never reaches. */
|
|
66
|
+
const ACHROMATIC = 0.02
|
|
67
|
+
|
|
68
|
+
function multiply(matrix: Matrix, vector: Vector): Vector {
|
|
69
|
+
const [row0, row1, row2] = matrix.map(
|
|
70
|
+
(row) => row[0] * vector[0] + row[1] * vector[1] + row[2] * vector[2],
|
|
71
|
+
)
|
|
72
|
+
return [row0 ?? 0, row1 ?? 0, row2 ?? 0]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function clamp(value: number, low: number, high: number): number {
|
|
76
|
+
return Math.min(high, Math.max(low, value))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function wrapHue(hue: number): number {
|
|
80
|
+
return ((hue % 360) + 360) % 360
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function hexToRgb(hex: string): Rgb {
|
|
84
|
+
return {
|
|
85
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
86
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
87
|
+
b: parseInt(hex.slice(4, 6), 16),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function rgbToHex({ r, g, b }: Rgb): string {
|
|
92
|
+
return [r, g, b]
|
|
93
|
+
.map((part) =>
|
|
94
|
+
Math.round(clamp(part, 0, 255))
|
|
95
|
+
.toString(16)
|
|
96
|
+
.padStart(2, '0'),
|
|
97
|
+
)
|
|
98
|
+
.join('')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* HSV off RGB. A color with no saturation has no hue of its own, so it takes
|
|
103
|
+
* the one handed in, which keeps a grey's hue slider where the operator left it.
|
|
104
|
+
*/
|
|
105
|
+
export function rgbToHsv({ r, g, b }: Rgb, hue = 0): Hsv {
|
|
106
|
+
const max = Math.max(r, g, b)
|
|
107
|
+
const delta = max - Math.min(r, g, b)
|
|
108
|
+
return {
|
|
109
|
+
h: delta === 0 ? hue : hueOf(r, g, b, max, delta),
|
|
110
|
+
s: max === 0 ? 0 : (delta / max) * 100,
|
|
111
|
+
v: (max / 255) * 100,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function hsvToRgb({ h, s, v }: Hsv): Rgb {
|
|
116
|
+
const value = (v / 100) * 255
|
|
117
|
+
const chroma = value * (s / 100)
|
|
118
|
+
return fromChroma(h, chroma, value - chroma)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** HSL off RGB, taking the hue handed in when there is no saturation. */
|
|
122
|
+
export function rgbToHsl({ r, g, b }: Rgb, hue = 0): Hsl {
|
|
123
|
+
const max = Math.max(r, g, b)
|
|
124
|
+
const min = Math.min(r, g, b)
|
|
125
|
+
const delta = max - min
|
|
126
|
+
const lightness = (max + min) / 2 / 255
|
|
127
|
+
const saturation =
|
|
128
|
+
delta === 0 ? 0 : delta / 255 / (1 - Math.abs(2 * lightness - 1))
|
|
129
|
+
return {
|
|
130
|
+
h: delta === 0 ? hue : hueOf(r, g, b, max, delta),
|
|
131
|
+
s: saturation * 100,
|
|
132
|
+
l: lightness * 100,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function hslToRgb({ h, s, l }: Hsl): Rgb {
|
|
137
|
+
const lightness = l / 100
|
|
138
|
+
const chroma = (1 - Math.abs(2 * lightness - 1)) * (s / 100) * 255
|
|
139
|
+
return fromChroma(h, chroma, lightness * 255 - chroma / 2)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hueOf(
|
|
143
|
+
r: number,
|
|
144
|
+
g: number,
|
|
145
|
+
b: number,
|
|
146
|
+
max: number,
|
|
147
|
+
delta: number,
|
|
148
|
+
): number {
|
|
149
|
+
const sector =
|
|
150
|
+
max === r
|
|
151
|
+
? (g - b) / delta + (g < b ? 6 : 0)
|
|
152
|
+
: max === g
|
|
153
|
+
? (b - r) / delta + 2
|
|
154
|
+
: (r - g) / delta + 4
|
|
155
|
+
return sector * 60
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function fromChroma(hue: number, chroma: number, base: number): Rgb {
|
|
159
|
+
const sector = wrapHue(hue) / 60
|
|
160
|
+
const second = chroma * (1 - Math.abs((sector % 2) - 1))
|
|
161
|
+
const [r, g, b]: Vector =
|
|
162
|
+
sector < 1
|
|
163
|
+
? [chroma, second, 0]
|
|
164
|
+
: sector < 2
|
|
165
|
+
? [second, chroma, 0]
|
|
166
|
+
: sector < 3
|
|
167
|
+
? [0, chroma, second]
|
|
168
|
+
: sector < 4
|
|
169
|
+
? [0, second, chroma]
|
|
170
|
+
: sector < 5
|
|
171
|
+
? [second, 0, chroma]
|
|
172
|
+
: [chroma, 0, second]
|
|
173
|
+
return { r: r + base, g: g + base, b: b + base }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function toLinear(channel: number): number {
|
|
177
|
+
const value = channel / 255
|
|
178
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function fromLinear(value: number): number {
|
|
182
|
+
const encoded =
|
|
183
|
+
value <= 0.0031308 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - 0.055
|
|
184
|
+
return encoded * 255
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** CIE LCH off RGB, taking the hue handed in when there is no chroma. */
|
|
188
|
+
export function rgbToLch({ r, g, b }: Rgb, hue = 0): Lch {
|
|
189
|
+
const xyz = multiply(
|
|
190
|
+
D65_TO_D50,
|
|
191
|
+
multiply(LINEAR_SRGB_TO_XYZ, [toLinear(r), toLinear(g), toLinear(b)]),
|
|
192
|
+
)
|
|
193
|
+
const f = (ratio: number): number =>
|
|
194
|
+
ratio > EPSILON ? Math.cbrt(ratio) : (KAPPA * ratio + 16) / 116
|
|
195
|
+
const fx = f(xyz[0] / D50_WHITE[0])
|
|
196
|
+
const fy = f(xyz[1] / D50_WHITE[1])
|
|
197
|
+
const fz = f(xyz[2] / D50_WHITE[2])
|
|
198
|
+
const a = 500 * (fx - fy)
|
|
199
|
+
const bAxis = 200 * (fy - fz)
|
|
200
|
+
const chroma = Math.sqrt(a * a + bAxis * bAxis)
|
|
201
|
+
return {
|
|
202
|
+
l: 116 * fy - 16,
|
|
203
|
+
c: chroma < ACHROMATIC ? 0 : chroma,
|
|
204
|
+
h:
|
|
205
|
+
chroma < ACHROMATIC
|
|
206
|
+
? hue
|
|
207
|
+
: wrapHue((Math.atan2(bAxis, a) * 180) / Math.PI),
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* RGB off CIE LCH. A color outside sRGB has no faithful hex, so each channel
|
|
213
|
+
* clamps and `isClamped` says so, letting the row show the color written.
|
|
214
|
+
*/
|
|
215
|
+
export function lchToRgb({ l, c, h }: Lch): {
|
|
216
|
+
readonly rgb: Rgb
|
|
217
|
+
readonly isClamped: boolean
|
|
218
|
+
} {
|
|
219
|
+
const radians = (h * Math.PI) / 180
|
|
220
|
+
const fy = (l + 16) / 116
|
|
221
|
+
const fx = fy + (c * Math.cos(radians)) / 500
|
|
222
|
+
const fz = fy - (c * Math.sin(radians)) / 200
|
|
223
|
+
const ratios: Vector = [
|
|
224
|
+
fx ** 3 > EPSILON ? fx ** 3 : (116 * fx - 16) / KAPPA,
|
|
225
|
+
l > KAPPA * EPSILON ? fy ** 3 : l / KAPPA,
|
|
226
|
+
fz ** 3 > EPSILON ? fz ** 3 : (116 * fz - 16) / KAPPA,
|
|
227
|
+
]
|
|
228
|
+
const xyz: Vector = [
|
|
229
|
+
ratios[0] * D50_WHITE[0],
|
|
230
|
+
ratios[1] * D50_WHITE[1],
|
|
231
|
+
ratios[2] * D50_WHITE[2],
|
|
232
|
+
]
|
|
233
|
+
const channels = multiply(XYZ_TO_LINEAR_SRGB, multiply(D50_TO_D65, xyz)).map(
|
|
234
|
+
fromLinear,
|
|
235
|
+
)
|
|
236
|
+
/* Half a step either side rounds into range, so it is not a clamp. */
|
|
237
|
+
const isClamped = channels.some((part) => part < -0.5 || part > 255.5)
|
|
238
|
+
const [r, g, b] = channels.map((part) => clamp(part, 0, 255))
|
|
239
|
+
return { rgb: { r: r ?? 0, g: g ?? 0, b: b ?? 0 }, isClamped }
|
|
240
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsxImportSource preact */
|
|
2
|
-
import type { JSX } from 'preact'
|
|
2
|
+
import type { ComponentChildren, JSX } from 'preact'
|
|
3
3
|
import { useRef } from 'preact/hooks'
|
|
4
4
|
import { scrubStep } from '@/canvas/client/inspector/values'
|
|
5
5
|
|
|
@@ -35,6 +35,8 @@ interface FieldProps {
|
|
|
35
35
|
readonly onCommit: (value: string) => void
|
|
36
36
|
/** Makes the glyph a drag handle. Typing stays the keyboard path. */
|
|
37
37
|
readonly scrub?: Scrub
|
|
38
|
+
/** A control inside the field's box after the value, such as a mode menu. */
|
|
39
|
+
readonly trailing?: ComponentChildren
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const NUMBER = /^-?\d*\.?\d+$/
|
|
@@ -69,6 +71,7 @@ export function Field({
|
|
|
69
71
|
placeholder,
|
|
70
72
|
onCommit,
|
|
71
73
|
scrub,
|
|
74
|
+
trailing,
|
|
72
75
|
}: FieldProps): JSX.Element {
|
|
73
76
|
const input = useRef<HTMLInputElement>(null)
|
|
74
77
|
const handle = useRef<HTMLSpanElement>(null)
|
|
@@ -169,6 +172,7 @@ export function Field({
|
|
|
169
172
|
if (event.key === 'Escape') event.currentTarget.value = initial
|
|
170
173
|
}}
|
|
171
174
|
/>
|
|
175
|
+
{trailing}
|
|
172
176
|
</div>
|
|
173
177
|
)
|
|
174
178
|
}
|