@kinetixui/cli 0.22.1 → 0.23.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/dist/index.js +2052 -54
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import pc10 from "picocolors";
|
|
6
6
|
|
|
7
7
|
// src/commands/add.ts
|
|
8
8
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -648,8 +648,8 @@ async function parity(names, options) {
|
|
|
648
648
|
return item;
|
|
649
649
|
}) : components;
|
|
650
650
|
const nameWidth = Math.max(9, ...rows.map((r) => r.name.length));
|
|
651
|
-
const
|
|
652
|
-
console.log(pc7.dim(
|
|
651
|
+
const header4 = `${"COMPONENT".padEnd(nameWidth)} ${PLATFORMS.map((p) => PLATFORM_ABBR[p]).join(" ")} STATUS`;
|
|
652
|
+
console.log(pc7.dim(header4));
|
|
653
653
|
for (const item of rows) {
|
|
654
654
|
const on = new Set(item.platforms ?? PLATFORMS);
|
|
655
655
|
const marks = PLATFORMS.map((p) => on.has(p) ? pc7.green("\u2713 ") : pc7.dim("\u2014 "));
|
|
@@ -666,20 +666,275 @@ async function parity(names, options) {
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
// src/commands/
|
|
670
|
-
import {
|
|
671
|
-
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
672
|
-
import path9 from "path";
|
|
669
|
+
// src/commands/preset.ts
|
|
670
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
673
671
|
import pc8 from "picocolors";
|
|
674
672
|
|
|
675
|
-
// src/
|
|
673
|
+
// ../create-preset/src/index.ts
|
|
674
|
+
var PRESET_VERSION = 1;
|
|
675
|
+
var SUPPORTED_VERSIONS = [1];
|
|
676
|
+
var NEUTRALS = ["kinetix", "neutral", "cool", "warm", "stone"];
|
|
677
|
+
var RADII = ["square", "small", "default", "rounded", "soft"];
|
|
678
|
+
var SURFACES = ["flat", "bordered", "soft", "elevated"];
|
|
679
|
+
var CHART_PALETTES = ["kinetix", "brand", "categorical", "cool", "warm"];
|
|
680
|
+
var ACCEPTED_TOKENS = [
|
|
681
|
+
"background",
|
|
682
|
+
"foreground",
|
|
683
|
+
"card",
|
|
684
|
+
"card-foreground",
|
|
685
|
+
"popover",
|
|
686
|
+
"popover-foreground",
|
|
687
|
+
"primary",
|
|
688
|
+
"primary-foreground",
|
|
689
|
+
"secondary",
|
|
690
|
+
"secondary-foreground",
|
|
691
|
+
"muted",
|
|
692
|
+
"muted-foreground",
|
|
693
|
+
"accent",
|
|
694
|
+
"accent-foreground",
|
|
695
|
+
"destructive",
|
|
696
|
+
"destructive-foreground",
|
|
697
|
+
"border",
|
|
698
|
+
"input",
|
|
699
|
+
"ring",
|
|
700
|
+
"action",
|
|
701
|
+
"action-foreground",
|
|
702
|
+
"link",
|
|
703
|
+
"focus",
|
|
704
|
+
"brand",
|
|
705
|
+
"brand-foreground"
|
|
706
|
+
];
|
|
707
|
+
var DEFAULT_BRAND = "#1d4ed8";
|
|
708
|
+
var DEFAULT_PRESET = {
|
|
709
|
+
brand: DEFAULT_BRAND,
|
|
710
|
+
neutral: "kinetix",
|
|
711
|
+
radius: "default",
|
|
712
|
+
surface: "soft",
|
|
713
|
+
chartPalette: "kinetix",
|
|
714
|
+
manualOverrides: {}
|
|
715
|
+
};
|
|
716
|
+
var LIMITS = {
|
|
717
|
+
/** Longest code accepted, prefix included. A maximal legitimate preset is ~700 characters. */
|
|
718
|
+
code: 4096,
|
|
719
|
+
/** Longest decoded JSON. Base64 expands by 4/3, so this is the real guard. */
|
|
720
|
+
json: 4096,
|
|
721
|
+
/** A hex colour, at most. */
|
|
722
|
+
colour: 7,
|
|
723
|
+
/** More override entries than there are tokens means the payload is not one of ours. */
|
|
724
|
+
overrides: 64
|
|
725
|
+
};
|
|
726
|
+
var HEX2 = /^#[0-9a-f]{6}$/;
|
|
727
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
728
|
+
var fail = (error) => ({ ok: false, error });
|
|
729
|
+
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
730
|
+
function normalizeColour(value) {
|
|
731
|
+
const trimmed = value.trim().toLowerCase();
|
|
732
|
+
const hashed = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
733
|
+
return HEX2.test(hashed) ? hashed : null;
|
|
734
|
+
}
|
|
735
|
+
function readEnum(value, allowed, field) {
|
|
736
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
737
|
+
return { code: "bad-field", field, message: `${field} must be one of: ${allowed.join(", ")}` };
|
|
738
|
+
}
|
|
739
|
+
return value;
|
|
740
|
+
}
|
|
741
|
+
function validate(raw) {
|
|
742
|
+
if (!isPlainObject(raw)) return fail({ code: "bad-json", message: "A preset must be a JSON object." });
|
|
743
|
+
const version = raw.v;
|
|
744
|
+
if (typeof version !== "number" || !Number.isInteger(version)) {
|
|
745
|
+
return fail({ code: "bad-json", message: "A preset must carry an integer version." });
|
|
746
|
+
}
|
|
747
|
+
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
748
|
+
return fail({
|
|
749
|
+
code: "unsupported-version",
|
|
750
|
+
version,
|
|
751
|
+
message: `This preset is version ${version}; this build reads version ${SUPPORTED_VERSIONS.join(", ")}. Update KinetixUI and try again.`
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
const config = { ...DEFAULT_PRESET, manualOverrides: {} };
|
|
755
|
+
if (raw.brand !== void 0) {
|
|
756
|
+
if (typeof raw.brand !== "string" || raw.brand.length > LIMITS.colour) {
|
|
757
|
+
return fail({ code: "bad-field", field: "brand", message: "brand must be a hex colour." });
|
|
758
|
+
}
|
|
759
|
+
const colour = normalizeColour(raw.brand);
|
|
760
|
+
if (!colour) return fail({ code: "bad-field", field: "brand", message: `"${raw.brand}" is not a six-digit hex colour.` });
|
|
761
|
+
config.brand = colour;
|
|
762
|
+
}
|
|
763
|
+
for (const [field, allowed] of [
|
|
764
|
+
["neutral", NEUTRALS],
|
|
765
|
+
["radius", RADII],
|
|
766
|
+
["surface", SURFACES],
|
|
767
|
+
["chartPalette", CHART_PALETTES]
|
|
768
|
+
]) {
|
|
769
|
+
if (raw[field] === void 0) continue;
|
|
770
|
+
const value = readEnum(raw[field], allowed, field);
|
|
771
|
+
if (typeof value !== "string") return fail(value);
|
|
772
|
+
config[field] = value;
|
|
773
|
+
}
|
|
774
|
+
if (raw.manualOverrides !== void 0) {
|
|
775
|
+
if (!isPlainObject(raw.manualOverrides)) {
|
|
776
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: "manualOverrides must be an object." });
|
|
777
|
+
}
|
|
778
|
+
const entries = Object.entries(raw.manualOverrides);
|
|
779
|
+
if (entries.length > LIMITS.overrides) {
|
|
780
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: "Too many overrides." });
|
|
781
|
+
}
|
|
782
|
+
for (const [token, value] of entries) {
|
|
783
|
+
if (FORBIDDEN_KEYS.has(token)) {
|
|
784
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: `"${token}" is not a theme colour.` });
|
|
785
|
+
}
|
|
786
|
+
if (!ACCEPTED_TOKENS.includes(token)) {
|
|
787
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: `"${token}" is not a theme colour.` });
|
|
788
|
+
}
|
|
789
|
+
if (typeof value !== "string" || value.length > LIMITS.colour) {
|
|
790
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: `${token} must be a hex colour.` });
|
|
791
|
+
}
|
|
792
|
+
const colour = normalizeColour(value);
|
|
793
|
+
if (!colour) {
|
|
794
|
+
return fail({ code: "bad-field", field: "manualOverrides", message: `${token}: "${value}" is not a six-digit hex colour.` });
|
|
795
|
+
}
|
|
796
|
+
config.manualOverrides[token] = colour;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return { ok: true, config };
|
|
800
|
+
}
|
|
801
|
+
function canonicalize(config) {
|
|
802
|
+
const envelope = { v: PRESET_VERSION };
|
|
803
|
+
const brand = normalizeColour(config.brand) ?? DEFAULT_BRAND;
|
|
804
|
+
if (brand !== DEFAULT_PRESET.brand) envelope.brand = brand;
|
|
805
|
+
if (config.neutral !== DEFAULT_PRESET.neutral) envelope.neutral = config.neutral;
|
|
806
|
+
if (config.radius !== DEFAULT_PRESET.radius) envelope.radius = config.radius;
|
|
807
|
+
if (config.surface !== DEFAULT_PRESET.surface) envelope.surface = config.surface;
|
|
808
|
+
if (config.chartPalette !== DEFAULT_PRESET.chartPalette) envelope.chartPalette = config.chartPalette;
|
|
809
|
+
const overrides = {};
|
|
810
|
+
for (const token of ACCEPTED_TOKENS) {
|
|
811
|
+
const value = config.manualOverrides[token];
|
|
812
|
+
if (!value) continue;
|
|
813
|
+
const colour = normalizeColour(value);
|
|
814
|
+
if (colour) overrides[token] = colour;
|
|
815
|
+
}
|
|
816
|
+
if (Object.keys(overrides).length > 0) envelope.manualOverrides = overrides;
|
|
817
|
+
return envelope;
|
|
818
|
+
}
|
|
819
|
+
function isDefaultPreset(config) {
|
|
820
|
+
const envelope = canonicalize(config);
|
|
821
|
+
return Object.keys(envelope).length === 1;
|
|
822
|
+
}
|
|
823
|
+
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
824
|
+
var REVERSE = new Map([...ALPHABET].map((c, i) => [c, i]));
|
|
825
|
+
function toBase64Url(text) {
|
|
826
|
+
const bytes = [];
|
|
827
|
+
for (let i = 0; i < text.length; i++) {
|
|
828
|
+
const code = text.charCodeAt(i);
|
|
829
|
+
if (code > 127) return null;
|
|
830
|
+
bytes.push(code);
|
|
831
|
+
}
|
|
832
|
+
let out = "";
|
|
833
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
834
|
+
const a = bytes[i];
|
|
835
|
+
const b = bytes[i + 1];
|
|
836
|
+
const c = bytes[i + 2];
|
|
837
|
+
out += ALPHABET[a >> 2];
|
|
838
|
+
out += ALPHABET[(a & 3) << 4 | (b ?? 0) >> 4];
|
|
839
|
+
if (b === void 0) break;
|
|
840
|
+
out += ALPHABET[(b & 15) << 2 | (c ?? 0) >> 6];
|
|
841
|
+
if (c === void 0) break;
|
|
842
|
+
out += ALPHABET[c & 63];
|
|
843
|
+
}
|
|
844
|
+
return out;
|
|
845
|
+
}
|
|
846
|
+
function fromBase64Url(code) {
|
|
847
|
+
let out = "";
|
|
848
|
+
let buffer = 0;
|
|
849
|
+
let bits = 0;
|
|
850
|
+
for (const char of code) {
|
|
851
|
+
const value = REVERSE.get(char);
|
|
852
|
+
if (value === void 0) return null;
|
|
853
|
+
buffer = buffer << 6 | value;
|
|
854
|
+
bits += 6;
|
|
855
|
+
if (bits >= 8) {
|
|
856
|
+
bits -= 8;
|
|
857
|
+
const byte = buffer >> bits & 255;
|
|
858
|
+
if (byte > 127) return null;
|
|
859
|
+
out += String.fromCharCode(byte);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return out;
|
|
863
|
+
}
|
|
864
|
+
var PRESET_PREFIX = "KX";
|
|
865
|
+
function encodePreset(config) {
|
|
866
|
+
const payload = toBase64Url(JSON.stringify(canonicalize(config)));
|
|
867
|
+
if (payload === null) throw new Error("Preset contains a value that cannot be encoded.");
|
|
868
|
+
return `${PRESET_PREFIX}${PRESET_VERSION}_${payload}`;
|
|
869
|
+
}
|
|
870
|
+
function decodePreset(input) {
|
|
871
|
+
const code = extractCode(input);
|
|
872
|
+
if (!code) return fail({ code: "empty", message: "No preset code found." });
|
|
873
|
+
if (code.length > LIMITS.code) return fail({ code: "too-long", message: "That preset code is too long to be one of ours." });
|
|
874
|
+
const match = /^KX(\d+)_(.*)$/s.exec(code);
|
|
875
|
+
if (!match) {
|
|
876
|
+
return fail({ code: "bad-prefix", message: "A preset code starts with KX1_." });
|
|
877
|
+
}
|
|
878
|
+
const version = Number(match[1]);
|
|
879
|
+
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
880
|
+
return fail({
|
|
881
|
+
code: "unsupported-version",
|
|
882
|
+
version,
|
|
883
|
+
message: `This preset is version ${version}; this build reads version ${SUPPORTED_VERSIONS.join(", ")}. Update KinetixUI and try again.`
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
const json = fromBase64Url(match[2] ?? "");
|
|
887
|
+
if (json === null) return fail({ code: "bad-encoding", message: "That preset code is not valid base64url." });
|
|
888
|
+
if (json.length > LIMITS.json) return fail({ code: "too-long", message: "That preset is larger than any real one." });
|
|
889
|
+
let parsed;
|
|
890
|
+
try {
|
|
891
|
+
parsed = JSON.parse(json);
|
|
892
|
+
} catch {
|
|
893
|
+
return fail({ code: "bad-json", message: "That preset code does not contain a readable preset." });
|
|
894
|
+
}
|
|
895
|
+
if (isPlainObject(parsed) && parsed.v === void 0) parsed.v = version;
|
|
896
|
+
const result = validate(parsed);
|
|
897
|
+
return result.ok ? { ok: true, config: migrate(result.config, version) } : result;
|
|
898
|
+
}
|
|
899
|
+
function migrate(config, from) {
|
|
900
|
+
switch (from) {
|
|
901
|
+
case 1:
|
|
902
|
+
return config;
|
|
903
|
+
default:
|
|
904
|
+
return config;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function extractCode(input) {
|
|
908
|
+
const text = input.trim();
|
|
909
|
+
if (!text) return null;
|
|
910
|
+
if (text.length > LIMITS.code * 2) return null;
|
|
911
|
+
if (/^https?:\/\//i.test(text)) {
|
|
912
|
+
try {
|
|
913
|
+
const url = new URL(text);
|
|
914
|
+
const values = url.searchParams.getAll(PRESET_PARAM);
|
|
915
|
+
const first = values[0];
|
|
916
|
+
return first === void 0 ? null : first.trim();
|
|
917
|
+
} catch {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return text;
|
|
922
|
+
}
|
|
923
|
+
var PRESET_PARAM = "preset";
|
|
924
|
+
function presetUrl(config, origin) {
|
|
925
|
+
return `${origin.replace(/\/$/, "")}/create?${PRESET_PARAM}=${encodePreset(config)}`;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// ../create-theme/src/color-math.ts
|
|
676
929
|
function hexToRgb(hex) {
|
|
677
930
|
const h = hex.replace("#", "");
|
|
678
931
|
const n = parseInt(h.length === 3 ? h.replace(/(.)/g, "$1$1") : h, 16);
|
|
679
932
|
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
680
933
|
}
|
|
681
934
|
function rgbToHsl(r, g, b) {
|
|
682
|
-
r /= 255
|
|
935
|
+
r /= 255;
|
|
936
|
+
g /= 255;
|
|
937
|
+
b /= 255;
|
|
683
938
|
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
684
939
|
let h = 0, s = 0;
|
|
685
940
|
const l = (max + min) / 2;
|
|
@@ -705,18 +960,46 @@ function hexToHslChannels(hex) {
|
|
|
705
960
|
const [h, s, l] = rgbToHsl(...hexToRgb(hex));
|
|
706
961
|
return `${h} ${s}% ${l}%`;
|
|
707
962
|
}
|
|
708
|
-
function
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
712
|
-
});
|
|
713
|
-
return 0.2126 * cr + 0.7152 * cg + 0.0722 * cb;
|
|
963
|
+
function relLuminanceNormalized([r, g, b]) {
|
|
964
|
+
const linear = (s) => s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
965
|
+
return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
|
|
714
966
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const
|
|
967
|
+
var normalize = ([r, g, b]) => [r / 255, g / 255, b / 255];
|
|
968
|
+
function contrastOfNormalized(a, b) {
|
|
969
|
+
const la = relLuminanceNormalized(a) + 0.05;
|
|
970
|
+
const lb = relLuminanceNormalized(b) + 0.05;
|
|
718
971
|
return la > lb ? la / lb : lb / la;
|
|
719
972
|
}
|
|
973
|
+
function contrastRatio(hexA, hexB) {
|
|
974
|
+
return contrastOfNormalized(normalize(hexToRgb(hexA)), normalize(hexToRgb(hexB)));
|
|
975
|
+
}
|
|
976
|
+
function hslChannelsToHex(channels) {
|
|
977
|
+
const [h = 0, s = 0, l = 0] = channels.trim().split(/\s+/).map((v) => parseFloat(v));
|
|
978
|
+
const S = s / 100;
|
|
979
|
+
const L = l / 100;
|
|
980
|
+
const c = (1 - Math.abs(2 * L - 1)) * S;
|
|
981
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
982
|
+
const m = L - c / 2;
|
|
983
|
+
const sector = Math.floor((h % 360 + 360) % 360 / 60);
|
|
984
|
+
const rgb = [
|
|
985
|
+
[c, x, 0],
|
|
986
|
+
[x, c, 0],
|
|
987
|
+
[0, c, x],
|
|
988
|
+
[0, x, c],
|
|
989
|
+
[x, 0, c],
|
|
990
|
+
[c, 0, x]
|
|
991
|
+
][sector];
|
|
992
|
+
return `#${rgb.map((v) => Math.round((v + m) * 255).toString(16).padStart(2, "0")).join("")}`;
|
|
993
|
+
}
|
|
994
|
+
function swiftUiChannels(hex) {
|
|
995
|
+
return hexToRgb(hex).map((v) => Number((v / 255).toFixed(3)));
|
|
996
|
+
}
|
|
997
|
+
function guaranteedContrast(hexA, hexB) {
|
|
998
|
+
const exact = contrastRatio(hexA, hexB);
|
|
999
|
+
const asCss = contrastRatio(hslChannelsToHex(hexToHslChannels(hexA)), hslChannelsToHex(hexToHslChannels(hexB)));
|
|
1000
|
+
const asSwift = contrastOfNormalized(swiftUiChannels(hexA), swiftUiChannels(hexB));
|
|
1001
|
+
return Math.min(exact, asCss, asSwift);
|
|
1002
|
+
}
|
|
720
1003
|
function bestTextHex(bgHex) {
|
|
721
1004
|
return contrastRatio(bgHex, "#ffffff") >= contrastRatio(bgHex, "#000000") ? "#ffffff" : "#000000";
|
|
722
1005
|
}
|
|
@@ -728,8 +1011,1672 @@ function normalizeHex(v) {
|
|
|
728
1011
|
return `#${t.toLowerCase()}`;
|
|
729
1012
|
}
|
|
730
1013
|
|
|
1014
|
+
// ../create-theme/src/oklch.ts
|
|
1015
|
+
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
1016
|
+
var wrapHue = (h) => h >= 0 && h < 360 ? h : (h % 360 + 360) % 360;
|
|
1017
|
+
var MAX_CHROMA = 0.37;
|
|
1018
|
+
var toLinear = (v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
1019
|
+
var toGamma = (v) => v <= 31308e-7 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055;
|
|
1020
|
+
function srgbToOklab([r, g, b]) {
|
|
1021
|
+
const lr = toLinear(r / 255);
|
|
1022
|
+
const lg = toLinear(g / 255);
|
|
1023
|
+
const lb = toLinear(b / 255);
|
|
1024
|
+
const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
|
|
1025
|
+
const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
|
|
1026
|
+
const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
|
|
1027
|
+
return {
|
|
1028
|
+
L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
1029
|
+
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
1030
|
+
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function oklabToSrgbRaw({ L, a, b }) {
|
|
1034
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
1035
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
1036
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
1037
|
+
return [
|
|
1038
|
+
toGamma(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s) * 255,
|
|
1039
|
+
toGamma(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s) * 255,
|
|
1040
|
+
toGamma(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s) * 255
|
|
1041
|
+
];
|
|
1042
|
+
}
|
|
1043
|
+
function oklabToOklch({ L, a, b }) {
|
|
1044
|
+
const c = Math.sqrt(a * a + b * b);
|
|
1045
|
+
if (c < 1e-6) return { l: L, c: 0, h: 0 };
|
|
1046
|
+
return { l: L, c, h: (Math.atan2(b, a) * 180 / Math.PI + 360) % 360 };
|
|
1047
|
+
}
|
|
1048
|
+
function oklchToOklab({ l, c, h }) {
|
|
1049
|
+
const rad = h * Math.PI / 180;
|
|
1050
|
+
return { L: l, a: c * Math.cos(rad), b: c * Math.sin(rad) };
|
|
1051
|
+
}
|
|
1052
|
+
var EPS = 0.5 / 255;
|
|
1053
|
+
var fitsSrgb = (rgb) => rgb.every((v) => v >= -EPS * 255 && v <= 255 + EPS * 255);
|
|
1054
|
+
function gamutMapOklch(colour) {
|
|
1055
|
+
const h = wrapHue(colour.h);
|
|
1056
|
+
const l = clamp(colour.l, 0, 1);
|
|
1057
|
+
const c = Math.max(0, colour.c);
|
|
1058
|
+
if (fitsSrgb(oklabToSrgbRaw(oklchToOklab({ l, c, h })))) return { l, c, h };
|
|
1059
|
+
let lo = 0;
|
|
1060
|
+
let hi = c;
|
|
1061
|
+
for (let i = 0; i < 24; i++) {
|
|
1062
|
+
const mid = (lo + hi) / 2;
|
|
1063
|
+
if (fitsSrgb(oklabToSrgbRaw(oklchToOklab({ l, c: mid, h })))) lo = mid;
|
|
1064
|
+
else hi = mid;
|
|
1065
|
+
}
|
|
1066
|
+
return { l, c: lo, h };
|
|
1067
|
+
}
|
|
1068
|
+
function rgbToHex([r, g, b]) {
|
|
1069
|
+
const ch = (v) => Math.round(clamp(v, 0, 255)).toString(16).padStart(2, "0");
|
|
1070
|
+
return `#${ch(r)}${ch(g)}${ch(b)}`;
|
|
1071
|
+
}
|
|
1072
|
+
function hexToOklch(hex) {
|
|
1073
|
+
if (!isHex(hex)) return null;
|
|
1074
|
+
return oklabToOklch(srgbToOklab(hexToRgb(normalizeHex(hex))));
|
|
1075
|
+
}
|
|
1076
|
+
function oklchToHex(colour) {
|
|
1077
|
+
return rgbToHex(oklabToSrgbRaw(oklchToOklab(gamutMapOklch(colour))));
|
|
1078
|
+
}
|
|
1079
|
+
function adjust(colour, { dl = 0, dc = 0, dh = 0 }) {
|
|
1080
|
+
return gamutMapOklch({
|
|
1081
|
+
l: clamp(colour.l + dl, 0, 1),
|
|
1082
|
+
c: Math.max(0, colour.c + dc),
|
|
1083
|
+
h: wrapHue(colour.h + dh)
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// ../tokens/dist/web/tokens.js
|
|
1088
|
+
var tokens = {
|
|
1089
|
+
"color": {
|
|
1090
|
+
"azure": {
|
|
1091
|
+
"0": "#f7faff",
|
|
1092
|
+
"50": "#eff6ff",
|
|
1093
|
+
"100": "#dbeafe",
|
|
1094
|
+
"150": "#cde1fd",
|
|
1095
|
+
"200": "#bfdbfe",
|
|
1096
|
+
"300": "#93c5fd",
|
|
1097
|
+
"400": "#60a5fa",
|
|
1098
|
+
"500": "#3b82f6",
|
|
1099
|
+
"600": "#2563eb",
|
|
1100
|
+
"700": "#1d4ed8",
|
|
1101
|
+
"800": "#1e40af",
|
|
1102
|
+
"850": "#1b3897",
|
|
1103
|
+
"900": "#1e3a8a",
|
|
1104
|
+
"950": "#172554",
|
|
1105
|
+
"1000": "#0f172a"
|
|
1106
|
+
},
|
|
1107
|
+
"blue": {
|
|
1108
|
+
"0": "#f6fbff",
|
|
1109
|
+
"50": "#f0f7ff",
|
|
1110
|
+
"100": "#92b2c8",
|
|
1111
|
+
"150": "#83a4b9",
|
|
1112
|
+
"200": "#7495ab",
|
|
1113
|
+
"300": "#57778d",
|
|
1114
|
+
"400": "#395a70",
|
|
1115
|
+
"500": "#1b3c53",
|
|
1116
|
+
"600": "#163042",
|
|
1117
|
+
"700": "#102432",
|
|
1118
|
+
"800": "#0b1821",
|
|
1119
|
+
"850": "#081219",
|
|
1120
|
+
"900": "#050c11",
|
|
1121
|
+
"950": "#030608",
|
|
1122
|
+
"1000": "#000000"
|
|
1123
|
+
},
|
|
1124
|
+
"green": {
|
|
1125
|
+
"0": "#ffffff",
|
|
1126
|
+
"50": "#f1f3f1",
|
|
1127
|
+
"100": "#e3e7e3",
|
|
1128
|
+
"150": "#d5dbd5",
|
|
1129
|
+
"200": "#c7cfc7",
|
|
1130
|
+
"300": "#acb8ab",
|
|
1131
|
+
"400": "#90a08f",
|
|
1132
|
+
"500": "#748873",
|
|
1133
|
+
"600": "#5d6d5c",
|
|
1134
|
+
"700": "#465245",
|
|
1135
|
+
"800": "#2e362e",
|
|
1136
|
+
"850": "#232923",
|
|
1137
|
+
"900": "#171b17",
|
|
1138
|
+
"950": "#0c0e0c",
|
|
1139
|
+
"1000": "#000000"
|
|
1140
|
+
},
|
|
1141
|
+
"taupe": {
|
|
1142
|
+
"0": "#ffffff",
|
|
1143
|
+
"50": "#fbf9f8",
|
|
1144
|
+
"100": "#f6f3f0",
|
|
1145
|
+
"150": "#f2ece9",
|
|
1146
|
+
"200": "#ede6e2",
|
|
1147
|
+
"300": "#e4dad3",
|
|
1148
|
+
"400": "#dbcdc5",
|
|
1149
|
+
"500": "#d2c1b6",
|
|
1150
|
+
"600": "#b4a499",
|
|
1151
|
+
"700": "#96867c",
|
|
1152
|
+
"800": "#78695f",
|
|
1153
|
+
"850": "#695a51",
|
|
1154
|
+
"900": "#5a4b42",
|
|
1155
|
+
"950": "#4b3d34",
|
|
1156
|
+
"1000": "#3c2e25"
|
|
1157
|
+
},
|
|
1158
|
+
"cream": {
|
|
1159
|
+
"0": "#ffffff",
|
|
1160
|
+
"50": "#fefefd",
|
|
1161
|
+
"100": "#fefdfc",
|
|
1162
|
+
"150": "#fdfbfa",
|
|
1163
|
+
"200": "#fdfaf9",
|
|
1164
|
+
"300": "#fbf8f5",
|
|
1165
|
+
"400": "#faf5f2",
|
|
1166
|
+
"500": "#f9f3ef",
|
|
1167
|
+
"600": "#e3d4ca",
|
|
1168
|
+
"700": "#cdb5a4",
|
|
1169
|
+
"800": "#b8957f",
|
|
1170
|
+
"850": "#ad866c",
|
|
1171
|
+
"900": "#a27659",
|
|
1172
|
+
"950": "#976747",
|
|
1173
|
+
"1000": "#8c5734"
|
|
1174
|
+
},
|
|
1175
|
+
"amber": {
|
|
1176
|
+
"0": "#ffffff",
|
|
1177
|
+
"50": "#fff8ee",
|
|
1178
|
+
"100": "#fff2dd",
|
|
1179
|
+
"150": "#ffebcb",
|
|
1180
|
+
"200": "#ffe4ba",
|
|
1181
|
+
"300": "#ffd798",
|
|
1182
|
+
"400": "#ffc975",
|
|
1183
|
+
"500": "#ffbc53",
|
|
1184
|
+
"600": "#d49c42",
|
|
1185
|
+
"700": "#aa7b32",
|
|
1186
|
+
"800": "#7f5b21",
|
|
1187
|
+
"850": "#6a4b19",
|
|
1188
|
+
"900": "#553a11",
|
|
1189
|
+
"950": "#3f2a08",
|
|
1190
|
+
"1000": "#2a1a00"
|
|
1191
|
+
},
|
|
1192
|
+
"red": {
|
|
1193
|
+
"0": "#fff9f9",
|
|
1194
|
+
"50": "#f9e1e1",
|
|
1195
|
+
"100": "#f4c9c9",
|
|
1196
|
+
"150": "#eeb1b1",
|
|
1197
|
+
"200": "#e89999",
|
|
1198
|
+
"300": "#dd6a6a",
|
|
1199
|
+
"400": "#d13a3a",
|
|
1200
|
+
"500": "#c60a0a",
|
|
1201
|
+
"600": "#9e0808",
|
|
1202
|
+
"700": "#770606",
|
|
1203
|
+
"800": "#4f0404",
|
|
1204
|
+
"850": "#3b0303",
|
|
1205
|
+
"900": "#280202",
|
|
1206
|
+
"950": "#140101",
|
|
1207
|
+
"1000": "#000000"
|
|
1208
|
+
},
|
|
1209
|
+
"neutral": {
|
|
1210
|
+
"0": "#ffffff",
|
|
1211
|
+
"50": "#fafafa",
|
|
1212
|
+
"100": "#f6f6f6",
|
|
1213
|
+
"200": "#ededed",
|
|
1214
|
+
"300": "#e0e0e0",
|
|
1215
|
+
"400": "#c2c2c2",
|
|
1216
|
+
"500": "#9e9e9e",
|
|
1217
|
+
"600": "#6d6d6d",
|
|
1218
|
+
"700": "#4f4f4f",
|
|
1219
|
+
"800": "#333333",
|
|
1220
|
+
"900": "#1f1f1f",
|
|
1221
|
+
"1000": "#000000"
|
|
1222
|
+
},
|
|
1223
|
+
"static": {
|
|
1224
|
+
"white": "#ffffff",
|
|
1225
|
+
"black": "#000000"
|
|
1226
|
+
},
|
|
1227
|
+
"background": "#ffffff",
|
|
1228
|
+
"foreground": "#050c11",
|
|
1229
|
+
"card": "#ffffff",
|
|
1230
|
+
"card-foreground": "#050c11",
|
|
1231
|
+
"popover": "#ffffff",
|
|
1232
|
+
"popover-foreground": "#050c11",
|
|
1233
|
+
"primary": "#1d4ed8",
|
|
1234
|
+
"primary-foreground": "#f0f7ff",
|
|
1235
|
+
"secondary": "#c7cfc7",
|
|
1236
|
+
"secondary-foreground": "#465245",
|
|
1237
|
+
"muted": "#f6f6f6",
|
|
1238
|
+
"muted-foreground": "#6d6d6d",
|
|
1239
|
+
"accent": "#f0f7ff",
|
|
1240
|
+
"accent-foreground": "#1d4ed8",
|
|
1241
|
+
"destructive": "#c60a0a",
|
|
1242
|
+
"destructive-foreground": "#fef3f2",
|
|
1243
|
+
"success": "#5d6d5c",
|
|
1244
|
+
"success-foreground": "#f1f3f1",
|
|
1245
|
+
"warning": "#7f5b21",
|
|
1246
|
+
"warning-foreground": "#fff8eb",
|
|
1247
|
+
"border": "#92b2c8",
|
|
1248
|
+
"input": "#92b2c8",
|
|
1249
|
+
"ring": "#1d4ed8",
|
|
1250
|
+
"semantic": {
|
|
1251
|
+
"error": "#ec5047",
|
|
1252
|
+
"on-error": "#fef3f2",
|
|
1253
|
+
"error-container": "#fef3f2",
|
|
1254
|
+
"on-error-container": "#ec5047",
|
|
1255
|
+
"warning-container": "#fff8eb",
|
|
1256
|
+
"on-warning-container": "#f97907",
|
|
1257
|
+
"success-container": "#f1f3f1",
|
|
1258
|
+
"on-success-container": "#465245",
|
|
1259
|
+
"warning": "#f97907",
|
|
1260
|
+
"info-container": "#f0f7ff",
|
|
1261
|
+
"on-info-container": "#395a70"
|
|
1262
|
+
},
|
|
1263
|
+
"chart-1": "#1b3c53",
|
|
1264
|
+
"chart-2": "#748873",
|
|
1265
|
+
"chart-3": "#ffbc53",
|
|
1266
|
+
"chart-4": "#d13a3a",
|
|
1267
|
+
"chart-5": "#b4a499",
|
|
1268
|
+
"chart-6": "#57778d",
|
|
1269
|
+
"chart-7": "#9e0808",
|
|
1270
|
+
"chart-8": "#465245",
|
|
1271
|
+
"sidebar": "#fafafa",
|
|
1272
|
+
"sidebar-foreground": "#050c11",
|
|
1273
|
+
"sidebar-primary": "#1d4ed8",
|
|
1274
|
+
"sidebar-primary-foreground": "#f0f7ff",
|
|
1275
|
+
"sidebar-accent": "#f0f7ff",
|
|
1276
|
+
"sidebar-accent-foreground": "#1d4ed8",
|
|
1277
|
+
"sidebar-border": "#e0e0e0",
|
|
1278
|
+
"sidebar-ring": "#1d4ed8",
|
|
1279
|
+
"tertiary": "#b0b0b0",
|
|
1280
|
+
"tertiary-foreground": "#f0f7ff",
|
|
1281
|
+
"info": "#57778d",
|
|
1282
|
+
"info-foreground": "#f0f7ff",
|
|
1283
|
+
"brand": "#1b3c53",
|
|
1284
|
+
"brand-foreground": "#f0f7ff",
|
|
1285
|
+
"action": "#1d4ed8",
|
|
1286
|
+
"action-foreground": "#f0f7ff",
|
|
1287
|
+
"action-hover": "#3460dc",
|
|
1288
|
+
"action-pressed": "#3f69de",
|
|
1289
|
+
"focus": "#1d4ed8",
|
|
1290
|
+
"link": "#1d4ed8"
|
|
1291
|
+
},
|
|
1292
|
+
"spacing": {
|
|
1293
|
+
"0": "0",
|
|
1294
|
+
"1": "4px",
|
|
1295
|
+
"2": "8px",
|
|
1296
|
+
"3": "12px",
|
|
1297
|
+
"4": "16px",
|
|
1298
|
+
"5": "20px",
|
|
1299
|
+
"6": "24px",
|
|
1300
|
+
"7": "28px",
|
|
1301
|
+
"8": "32px",
|
|
1302
|
+
"10": "40px",
|
|
1303
|
+
"12": "48px",
|
|
1304
|
+
"16": "64px",
|
|
1305
|
+
"20": "80px",
|
|
1306
|
+
"24": "96px",
|
|
1307
|
+
"32": "128px"
|
|
1308
|
+
},
|
|
1309
|
+
"radius": {
|
|
1310
|
+
"none": "0",
|
|
1311
|
+
"sm": "4px",
|
|
1312
|
+
"md": "8px",
|
|
1313
|
+
"lg": "12px",
|
|
1314
|
+
"xl": "16px",
|
|
1315
|
+
"xxl": "24px",
|
|
1316
|
+
"full": "9999px",
|
|
1317
|
+
"field": "4px",
|
|
1318
|
+
"control": "8px",
|
|
1319
|
+
"container": "12px",
|
|
1320
|
+
"surface": "16px"
|
|
1321
|
+
},
|
|
1322
|
+
"borderWidth": {
|
|
1323
|
+
"default": "1px",
|
|
1324
|
+
"focus": "2px"
|
|
1325
|
+
},
|
|
1326
|
+
"interaction": {
|
|
1327
|
+
"target": {
|
|
1328
|
+
"minimum": "44px",
|
|
1329
|
+
"default": "40px"
|
|
1330
|
+
},
|
|
1331
|
+
"focus": {
|
|
1332
|
+
"width": "1px",
|
|
1333
|
+
"offset": "2px"
|
|
1334
|
+
},
|
|
1335
|
+
"press": {
|
|
1336
|
+
"opacity": 0.8
|
|
1337
|
+
},
|
|
1338
|
+
"drag": {
|
|
1339
|
+
"threshold": 4
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
"duration": {
|
|
1343
|
+
"instant": "100ms",
|
|
1344
|
+
"fast": "200ms",
|
|
1345
|
+
"base": "300ms",
|
|
1346
|
+
"slow": "500ms",
|
|
1347
|
+
"slower": "1000ms"
|
|
1348
|
+
},
|
|
1349
|
+
"easing": {
|
|
1350
|
+
"linear": [
|
|
1351
|
+
0,
|
|
1352
|
+
0,
|
|
1353
|
+
1,
|
|
1354
|
+
1
|
|
1355
|
+
],
|
|
1356
|
+
"standard": [
|
|
1357
|
+
0.4,
|
|
1358
|
+
0,
|
|
1359
|
+
0.2,
|
|
1360
|
+
1
|
|
1361
|
+
],
|
|
1362
|
+
"enter": [
|
|
1363
|
+
0,
|
|
1364
|
+
0,
|
|
1365
|
+
0.2,
|
|
1366
|
+
1
|
|
1367
|
+
],
|
|
1368
|
+
"exit": [
|
|
1369
|
+
0.4,
|
|
1370
|
+
0,
|
|
1371
|
+
1,
|
|
1372
|
+
1
|
|
1373
|
+
],
|
|
1374
|
+
"emphasized": [
|
|
1375
|
+
0.2,
|
|
1376
|
+
0,
|
|
1377
|
+
0,
|
|
1378
|
+
1
|
|
1379
|
+
]
|
|
1380
|
+
},
|
|
1381
|
+
"opacity": {
|
|
1382
|
+
"hidden": 0,
|
|
1383
|
+
"disabled": 0.5,
|
|
1384
|
+
"muted": 0.7,
|
|
1385
|
+
"visible": 1
|
|
1386
|
+
},
|
|
1387
|
+
"font": {
|
|
1388
|
+
"family": {
|
|
1389
|
+
"sans": "Montserrat, ui-sans-serif, system-ui, sans-serif"
|
|
1390
|
+
},
|
|
1391
|
+
"weight": {
|
|
1392
|
+
"regular": "400",
|
|
1393
|
+
"medium": "500",
|
|
1394
|
+
"semibold": "600"
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
"fontSize": {
|
|
1398
|
+
"display-lg": "57px",
|
|
1399
|
+
"display-md": "45px",
|
|
1400
|
+
"display-sm": "36px",
|
|
1401
|
+
"headline-lg": "32px",
|
|
1402
|
+
"headline-md": "28px",
|
|
1403
|
+
"headline-sm": "24px",
|
|
1404
|
+
"title-lg": "22px",
|
|
1405
|
+
"title-md": "16px",
|
|
1406
|
+
"title-sm": "14px",
|
|
1407
|
+
"title-dialog": "18px",
|
|
1408
|
+
"label-lg": "14px",
|
|
1409
|
+
"label-md": "12px",
|
|
1410
|
+
"label-sm": "11px",
|
|
1411
|
+
"body-lg": "16px",
|
|
1412
|
+
"body-md": "14px",
|
|
1413
|
+
"body-sm": "12px"
|
|
1414
|
+
},
|
|
1415
|
+
"lineHeight": {
|
|
1416
|
+
"display-lg": "64px",
|
|
1417
|
+
"display-md": "52px",
|
|
1418
|
+
"display-sm": "44px",
|
|
1419
|
+
"headline-lg": "40px",
|
|
1420
|
+
"headline-md": "36px",
|
|
1421
|
+
"headline-sm": "32px",
|
|
1422
|
+
"title-lg": "28px",
|
|
1423
|
+
"title-md": "24px",
|
|
1424
|
+
"title-sm": "20px",
|
|
1425
|
+
"title-dialog": "24px",
|
|
1426
|
+
"label-lg": "20px",
|
|
1427
|
+
"label-md": "16px",
|
|
1428
|
+
"label-sm": "16px",
|
|
1429
|
+
"body-lg": "24px",
|
|
1430
|
+
"body-md": "20px",
|
|
1431
|
+
"body-sm": "16px"
|
|
1432
|
+
},
|
|
1433
|
+
"letterSpacing": {
|
|
1434
|
+
"tighter": "-0.25px",
|
|
1435
|
+
"normal": "0",
|
|
1436
|
+
"wide-1": "0.1px",
|
|
1437
|
+
"wide-15": "0.15px",
|
|
1438
|
+
"wide-25": "0.25px",
|
|
1439
|
+
"wide-5": "0.5px"
|
|
1440
|
+
},
|
|
1441
|
+
"zIndex": {
|
|
1442
|
+
"base": 0,
|
|
1443
|
+
"raised": 1,
|
|
1444
|
+
"docked": 10,
|
|
1445
|
+
"focus": 20,
|
|
1446
|
+
"sticky": 40,
|
|
1447
|
+
"overlay": 50
|
|
1448
|
+
},
|
|
1449
|
+
"shadow": {
|
|
1450
|
+
"sm": [
|
|
1451
|
+
{
|
|
1452
|
+
"color": "#0000000d",
|
|
1453
|
+
"offsetX": "0",
|
|
1454
|
+
"offsetY": "1",
|
|
1455
|
+
"blur": "2",
|
|
1456
|
+
"spread": "0"
|
|
1457
|
+
}
|
|
1458
|
+
],
|
|
1459
|
+
"md": [
|
|
1460
|
+
{
|
|
1461
|
+
"color": "#0000001f",
|
|
1462
|
+
"offsetX": "0",
|
|
1463
|
+
"offsetY": "1",
|
|
1464
|
+
"blur": "1",
|
|
1465
|
+
"spread": "0"
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
"color": "#676e7614",
|
|
1469
|
+
"offsetX": "0",
|
|
1470
|
+
"offsetY": "2",
|
|
1471
|
+
"blur": "5",
|
|
1472
|
+
"spread": "0"
|
|
1473
|
+
}
|
|
1474
|
+
],
|
|
1475
|
+
"lg": [
|
|
1476
|
+
{
|
|
1477
|
+
"color": "#00000012",
|
|
1478
|
+
"offsetX": "0",
|
|
1479
|
+
"offsetY": "4",
|
|
1480
|
+
"blur": "6",
|
|
1481
|
+
"spread": "-1"
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
"color": "#0000000f",
|
|
1485
|
+
"offsetX": "0",
|
|
1486
|
+
"offsetY": "2",
|
|
1487
|
+
"blur": "4",
|
|
1488
|
+
"spread": "-2"
|
|
1489
|
+
}
|
|
1490
|
+
],
|
|
1491
|
+
"xl": [
|
|
1492
|
+
{
|
|
1493
|
+
"color": "#0000001f",
|
|
1494
|
+
"offsetX": "0",
|
|
1495
|
+
"offsetY": "4",
|
|
1496
|
+
"blur": "24",
|
|
1497
|
+
"spread": "0"
|
|
1498
|
+
}
|
|
1499
|
+
],
|
|
1500
|
+
"focus": [
|
|
1501
|
+
{
|
|
1502
|
+
"color": "#1d4ed8",
|
|
1503
|
+
"offsetX": "0",
|
|
1504
|
+
"offsetY": "0",
|
|
1505
|
+
"blur": "0",
|
|
1506
|
+
"spread": "1px"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
"color": "#1d4ed833",
|
|
1510
|
+
"offsetX": "0",
|
|
1511
|
+
"offsetY": "0",
|
|
1512
|
+
"blur": "0",
|
|
1513
|
+
"spread": "4"
|
|
1514
|
+
}
|
|
1515
|
+
],
|
|
1516
|
+
"focus-destructive": [
|
|
1517
|
+
{
|
|
1518
|
+
"color": "#ec5047",
|
|
1519
|
+
"offsetX": "0",
|
|
1520
|
+
"offsetY": "0",
|
|
1521
|
+
"blur": "0",
|
|
1522
|
+
"spread": "1px"
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
"color": "#ec504733",
|
|
1526
|
+
"offsetX": "0",
|
|
1527
|
+
"offsetY": "0",
|
|
1528
|
+
"blur": "0",
|
|
1529
|
+
"spread": "4"
|
|
1530
|
+
}
|
|
1531
|
+
],
|
|
1532
|
+
"focus-success": [
|
|
1533
|
+
{
|
|
1534
|
+
"color": "#5d6d5c",
|
|
1535
|
+
"offsetX": "0",
|
|
1536
|
+
"offsetY": "0",
|
|
1537
|
+
"blur": "0",
|
|
1538
|
+
"spread": "1px"
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
"color": "#5d6d5c33",
|
|
1542
|
+
"offsetX": "0",
|
|
1543
|
+
"offsetY": "0",
|
|
1544
|
+
"blur": "0",
|
|
1545
|
+
"spread": "4"
|
|
1546
|
+
}
|
|
1547
|
+
],
|
|
1548
|
+
"focus-warning": [
|
|
1549
|
+
{
|
|
1550
|
+
"color": "#7f5b21",
|
|
1551
|
+
"offsetX": "0",
|
|
1552
|
+
"offsetY": "0",
|
|
1553
|
+
"blur": "0",
|
|
1554
|
+
"spread": "1px"
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
"color": "#7f5b2133",
|
|
1558
|
+
"offsetX": "0",
|
|
1559
|
+
"offsetY": "0",
|
|
1560
|
+
"blur": "0",
|
|
1561
|
+
"spread": "4"
|
|
1562
|
+
}
|
|
1563
|
+
]
|
|
1564
|
+
},
|
|
1565
|
+
"text": {
|
|
1566
|
+
"display-lg": {
|
|
1567
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1568
|
+
"fontWeight": "500",
|
|
1569
|
+
"fontSize": "57px",
|
|
1570
|
+
"lineHeight": "64px",
|
|
1571
|
+
"letterSpacing": "-0.25px"
|
|
1572
|
+
},
|
|
1573
|
+
"display-md": {
|
|
1574
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1575
|
+
"fontWeight": "500",
|
|
1576
|
+
"fontSize": "45px",
|
|
1577
|
+
"lineHeight": "52px",
|
|
1578
|
+
"letterSpacing": "0"
|
|
1579
|
+
},
|
|
1580
|
+
"display-sm": {
|
|
1581
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1582
|
+
"fontWeight": "400",
|
|
1583
|
+
"fontSize": "36px",
|
|
1584
|
+
"lineHeight": "44px",
|
|
1585
|
+
"letterSpacing": "0"
|
|
1586
|
+
},
|
|
1587
|
+
"headline-lg": {
|
|
1588
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1589
|
+
"fontWeight": "600",
|
|
1590
|
+
"fontSize": "32px",
|
|
1591
|
+
"lineHeight": "40px",
|
|
1592
|
+
"letterSpacing": "0"
|
|
1593
|
+
},
|
|
1594
|
+
"headline-md": {
|
|
1595
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1596
|
+
"fontWeight": "400",
|
|
1597
|
+
"fontSize": "28px",
|
|
1598
|
+
"lineHeight": "36px",
|
|
1599
|
+
"letterSpacing": "0"
|
|
1600
|
+
},
|
|
1601
|
+
"headline-sm": {
|
|
1602
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1603
|
+
"fontWeight": "400",
|
|
1604
|
+
"fontSize": "24px",
|
|
1605
|
+
"lineHeight": "32px",
|
|
1606
|
+
"letterSpacing": "0"
|
|
1607
|
+
},
|
|
1608
|
+
"title-lg": {
|
|
1609
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1610
|
+
"fontWeight": "400",
|
|
1611
|
+
"fontSize": "22px",
|
|
1612
|
+
"lineHeight": "28px",
|
|
1613
|
+
"letterSpacing": "0"
|
|
1614
|
+
},
|
|
1615
|
+
"title-md": {
|
|
1616
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1617
|
+
"fontWeight": "500",
|
|
1618
|
+
"fontSize": "16px",
|
|
1619
|
+
"lineHeight": "24px",
|
|
1620
|
+
"letterSpacing": "0.15px"
|
|
1621
|
+
},
|
|
1622
|
+
"title-sm": {
|
|
1623
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1624
|
+
"fontWeight": "500",
|
|
1625
|
+
"fontSize": "14px",
|
|
1626
|
+
"lineHeight": "20px",
|
|
1627
|
+
"letterSpacing": "0.1px"
|
|
1628
|
+
},
|
|
1629
|
+
"title-dialog": {
|
|
1630
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1631
|
+
"fontWeight": "600",
|
|
1632
|
+
"fontSize": "18px",
|
|
1633
|
+
"lineHeight": "24px",
|
|
1634
|
+
"letterSpacing": "0"
|
|
1635
|
+
},
|
|
1636
|
+
"label-lg": {
|
|
1637
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1638
|
+
"fontWeight": "500",
|
|
1639
|
+
"fontSize": "14px",
|
|
1640
|
+
"lineHeight": "20px",
|
|
1641
|
+
"letterSpacing": "0.1px"
|
|
1642
|
+
},
|
|
1643
|
+
"label-md": {
|
|
1644
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1645
|
+
"fontWeight": "500",
|
|
1646
|
+
"fontSize": "12px",
|
|
1647
|
+
"lineHeight": "16px",
|
|
1648
|
+
"letterSpacing": "0.5px"
|
|
1649
|
+
},
|
|
1650
|
+
"label-sm": {
|
|
1651
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1652
|
+
"fontWeight": "500",
|
|
1653
|
+
"fontSize": "11px",
|
|
1654
|
+
"lineHeight": "16px",
|
|
1655
|
+
"letterSpacing": "0.5px"
|
|
1656
|
+
},
|
|
1657
|
+
"body-lg": {
|
|
1658
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1659
|
+
"fontWeight": "400",
|
|
1660
|
+
"fontSize": "16px",
|
|
1661
|
+
"lineHeight": "24px",
|
|
1662
|
+
"letterSpacing": "0.5px"
|
|
1663
|
+
},
|
|
1664
|
+
"body-md": {
|
|
1665
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1666
|
+
"fontWeight": "400",
|
|
1667
|
+
"fontSize": "14px",
|
|
1668
|
+
"lineHeight": "20px",
|
|
1669
|
+
"letterSpacing": "0.25px"
|
|
1670
|
+
},
|
|
1671
|
+
"body-sm": {
|
|
1672
|
+
"fontFamily": "Montserrat, ui-sans-serif, system-ui, sans-serif",
|
|
1673
|
+
"fontWeight": "400",
|
|
1674
|
+
"fontSize": "12px",
|
|
1675
|
+
"lineHeight": "16px",
|
|
1676
|
+
"letterSpacing": "0"
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
var tokens_default = tokens;
|
|
1681
|
+
|
|
1682
|
+
// ../create-theme/src/contract.ts
|
|
1683
|
+
var TOKEN_CONTRACT = [
|
|
1684
|
+
{ token: "background", light: "#ffffff", dark: "#050c11" },
|
|
1685
|
+
{ token: "foreground", light: "#050c11", dark: "#f0f7ff" },
|
|
1686
|
+
{ token: "card", light: "#ffffff", dark: "#0b1821", note: "synthesized" },
|
|
1687
|
+
{ token: "popover", light: "#ffffff", dark: "#0b1821", note: "synthesized" },
|
|
1688
|
+
{ token: "primary", light: "#1d4ed8", dark: "#60a5fa" },
|
|
1689
|
+
{ token: "primary-foreground", light: "#f0f7ff", dark: "#050c11" },
|
|
1690
|
+
{ token: "secondary", light: "#c7cfc7", dark: "#2e362e" },
|
|
1691
|
+
{ token: "secondary-foreground", light: "#465245", dark: "#e3e7e3" },
|
|
1692
|
+
{ token: "muted", light: "#f6f6f6", dark: "#102432" },
|
|
1693
|
+
{ token: "muted-foreground", light: "#6d6d6d", dark: "#92b2c8" },
|
|
1694
|
+
{ token: "accent", light: "#f0f7ff", dark: "#102432" },
|
|
1695
|
+
{ token: "accent-foreground", light: "#1d4ed8", dark: "#f0f7ff" },
|
|
1696
|
+
{ token: "destructive", light: "#c60a0a", dark: "#dd6a6a", note: "darkened for AA" },
|
|
1697
|
+
{ token: "success", light: "#5d6d5c", dark: "#90a08f" },
|
|
1698
|
+
{ token: "warning", light: "#7f5b21", dark: "#ffc975", note: "darkened for AA" },
|
|
1699
|
+
{ token: "border", light: "#92b2c8", dark: "#395a70" },
|
|
1700
|
+
{ token: "ring", light: "#1d4ed8", dark: "#60a5fa", note: "synthesized" },
|
|
1701
|
+
// Role tokens layered over primary / ring (added after the shadcn-compatible set on purpose: the
|
|
1702
|
+
// first row for a given hex names it in tokenNameForHex, so `primary` stays the label).
|
|
1703
|
+
{ token: "brand", light: "#1b3c53", dark: "#7495ab", note: "identity \u2014 the Figma navy" },
|
|
1704
|
+
{ token: "brand-foreground", light: "#f0f7ff", dark: "#050c11" },
|
|
1705
|
+
{ token: "action", light: "#1d4ed8", dark: "#60a5fa", note: "defaults to primary" },
|
|
1706
|
+
{ token: "action-foreground", light: "#f0f7ff", dark: "#050c11", note: "defaults to primary-foreground" },
|
|
1707
|
+
{ token: "action-hover", light: "#3460dc", dark: "#5796e3", note: "action @ 90% \u2014 native" },
|
|
1708
|
+
{ token: "action-pressed", light: "#3f69de", dark: "#528ed7", note: "action @ 85% \u2014 native" },
|
|
1709
|
+
{ token: "link", light: "#1d4ed8", dark: "#60a5fa", note: "defaults to primary" },
|
|
1710
|
+
{ token: "focus", light: "#1d4ed8", dark: "#60a5fa", note: "defaults to ring" }
|
|
1711
|
+
];
|
|
1712
|
+
var RADIUS_STEP_NAMES = ["sm", "md", "lg", "xl"];
|
|
1713
|
+
var ELEVATION_STEP_NAMES = ["sm", "md", "lg", "xl"];
|
|
1714
|
+
var px = (value) => Number(value.replace("px", ""));
|
|
1715
|
+
var SHIPPED_RADIUS = {
|
|
1716
|
+
sm: px(tokens_default.radius.sm),
|
|
1717
|
+
md: px(tokens_default.radius.md),
|
|
1718
|
+
lg: px(tokens_default.radius.lg),
|
|
1719
|
+
xl: px(tokens_default.radius.xl)
|
|
1720
|
+
};
|
|
1721
|
+
var SHADOWS = tokens_default.shadow;
|
|
1722
|
+
var SHIPPED_ELEVATION = {
|
|
1723
|
+
sm: SHADOWS.sm,
|
|
1724
|
+
md: SHADOWS.md,
|
|
1725
|
+
lg: SHADOWS.lg,
|
|
1726
|
+
xl: SHADOWS.xl
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
// ../create-theme/src/palette.ts
|
|
1730
|
+
var CONTRAST_PAIRS = [
|
|
1731
|
+
["background", "foreground"],
|
|
1732
|
+
["card", "card-foreground"],
|
|
1733
|
+
["popover", "popover-foreground"],
|
|
1734
|
+
["primary", "primary-foreground"],
|
|
1735
|
+
["secondary", "secondary-foreground"],
|
|
1736
|
+
["muted", "muted-foreground"],
|
|
1737
|
+
["accent", "accent-foreground"],
|
|
1738
|
+
["destructive", "destructive-foreground"],
|
|
1739
|
+
["action", "action-foreground"],
|
|
1740
|
+
["brand", "brand-foreground"]
|
|
1741
|
+
];
|
|
1742
|
+
|
|
1743
|
+
// ../create-theme/src/engine.ts
|
|
1744
|
+
var ACTION_BAND = {
|
|
1745
|
+
light: [0.42, 0.62],
|
|
1746
|
+
dark: [0.66, 0.84]
|
|
1747
|
+
};
|
|
1748
|
+
var BACKDROP_L = { light: 1, dark: 0.15 };
|
|
1749
|
+
var clamp2 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
1750
|
+
function intoBand(colour, mode) {
|
|
1751
|
+
const [lo, hi] = ACTION_BAND[mode];
|
|
1752
|
+
return adjust(colour, { dl: clamp2(colour.l, lo, hi) - colour.l });
|
|
1753
|
+
}
|
|
1754
|
+
function foregroundFor(surfaceHex) {
|
|
1755
|
+
return foregroundForAll([surfaceHex]);
|
|
1756
|
+
}
|
|
1757
|
+
function foregroundForAll(surfaces) {
|
|
1758
|
+
const primary = surfaces[0];
|
|
1759
|
+
if (primary === void 0) return "#000000";
|
|
1760
|
+
const s = hexToOklch(primary);
|
|
1761
|
+
if (!s) return "#000000";
|
|
1762
|
+
const tint = Math.min(s.c, 0.03);
|
|
1763
|
+
const worst = (candidate) => Math.min(...surfaces.map((surface) => guaranteedContrast(surface, candidate)));
|
|
1764
|
+
const tinted = [oklchToHex({ l: 0.97, c: tint, h: s.h }), oklchToHex({ l: 0.09, c: tint, h: s.h })];
|
|
1765
|
+
const best = worst(tinted[0]) >= worst(tinted[1]) ? tinted[0] : tinted[1];
|
|
1766
|
+
if (worst(best) >= AA) return best;
|
|
1767
|
+
return worst("#ffffff") >= worst("#000000") ? "#ffffff" : "#000000";
|
|
1768
|
+
}
|
|
1769
|
+
var AA = 4.5;
|
|
1770
|
+
function stateOf(action, mode, fraction) {
|
|
1771
|
+
return adjust(action, { dl: (BACKDROP_L[mode] - action.l) * fraction });
|
|
1772
|
+
}
|
|
1773
|
+
var HOVER_FRACTION = 0.1;
|
|
1774
|
+
var PRESSED_FRACTION = 0.16;
|
|
1775
|
+
var STATE_SCALE_STEPS = 20;
|
|
1776
|
+
function interactionStates(action, mode) {
|
|
1777
|
+
const actionHex = oklchToHex(action);
|
|
1778
|
+
for (let step = STATE_SCALE_STEPS; step >= 0; step--) {
|
|
1779
|
+
const scale = step / STATE_SCALE_STEPS;
|
|
1780
|
+
const hover = oklchToHex(stateOf(action, mode, HOVER_FRACTION * scale));
|
|
1781
|
+
const pressed = oklchToHex(stateOf(action, mode, PRESSED_FRACTION * scale));
|
|
1782
|
+
const foreground2 = foregroundForAll([actionHex, hover, pressed]);
|
|
1783
|
+
const readable = [actionHex, hover, pressed].every((surface) => guaranteedContrast(surface, foreground2) >= AA);
|
|
1784
|
+
if (readable) return { hover, pressed, foreground: foreground2, scale };
|
|
1785
|
+
}
|
|
1786
|
+
const foreground = foregroundForAll([actionHex]);
|
|
1787
|
+
return { hover: actionHex, pressed: actionHex, foreground, scale: 0 };
|
|
1788
|
+
}
|
|
1789
|
+
function readableOnSurface(surfaceHex, preferredHex) {
|
|
1790
|
+
if (guaranteedContrast(surfaceHex, preferredHex) >= AA) return preferredHex;
|
|
1791
|
+
const surface = hexToOklch(surfaceHex);
|
|
1792
|
+
const preferred = hexToOklch(preferredHex);
|
|
1793
|
+
if (!surface || !preferred) return foregroundFor(surfaceHex);
|
|
1794
|
+
const step = surface.l > 0.5 ? -0.02 : 0.02;
|
|
1795
|
+
for (let i = 1; i <= 50; i++) {
|
|
1796
|
+
const candidate = oklchToHex(adjust(preferred, { dl: step * i }));
|
|
1797
|
+
if (guaranteedContrast(surfaceHex, candidate) >= AA) return candidate;
|
|
1798
|
+
}
|
|
1799
|
+
return foregroundFor(surfaceHex);
|
|
1800
|
+
}
|
|
1801
|
+
function deriveBrandRoles(brandHex, mode) {
|
|
1802
|
+
const brand = hexToOklch(brandHex);
|
|
1803
|
+
if (!brand) return {};
|
|
1804
|
+
const action = intoBand(brand, mode);
|
|
1805
|
+
const actionHex = oklchToHex(action);
|
|
1806
|
+
const brandOwn = oklchToHex(brand);
|
|
1807
|
+
const states = interactionStates(action, mode);
|
|
1808
|
+
const actionFg = states.foreground;
|
|
1809
|
+
const accent = oklchToHex({
|
|
1810
|
+
l: mode === "light" ? 0.96 : 0.24,
|
|
1811
|
+
c: Math.min(brand.c, 0.04),
|
|
1812
|
+
h: brand.h
|
|
1813
|
+
});
|
|
1814
|
+
return {
|
|
1815
|
+
brand: brandOwn,
|
|
1816
|
+
"brand-foreground": foregroundFor(brandOwn),
|
|
1817
|
+
action: actionHex,
|
|
1818
|
+
"action-foreground": actionFg,
|
|
1819
|
+
"action-hover": states.hover,
|
|
1820
|
+
"action-pressed": states.pressed,
|
|
1821
|
+
link: actionHex,
|
|
1822
|
+
focus: actionHex,
|
|
1823
|
+
// `primary` and `ring` are the pre-role names the contract says `action` and `focus` default to.
|
|
1824
|
+
// Create writes both so a component that still reads the older name follows the theme.
|
|
1825
|
+
primary: actionHex,
|
|
1826
|
+
"primary-foreground": actionFg,
|
|
1827
|
+
ring: actionHex,
|
|
1828
|
+
accent,
|
|
1829
|
+
// The action colour if it is readable on its own tint, stepped toward readable if not.
|
|
1830
|
+
"accent-foreground": readableOnSurface(accent, actionHex)
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
var NEUTRAL_TINT = {
|
|
1834
|
+
neutral: { h: 0, c: 0 },
|
|
1835
|
+
cool: { h: 250, c: 0.012 },
|
|
1836
|
+
warm: { h: 70, c: 0.012 },
|
|
1837
|
+
stone: { h: 40, c: 8e-3 }
|
|
1838
|
+
};
|
|
1839
|
+
var NEUTRAL_LADDER = {
|
|
1840
|
+
light: {
|
|
1841
|
+
background: 1,
|
|
1842
|
+
card: 1,
|
|
1843
|
+
popover: 1,
|
|
1844
|
+
muted: 0.975,
|
|
1845
|
+
border: 0.86,
|
|
1846
|
+
input: 0.86,
|
|
1847
|
+
"muted-foreground": 0.535,
|
|
1848
|
+
foreground: 0.15
|
|
1849
|
+
},
|
|
1850
|
+
dark: {
|
|
1851
|
+
background: 0.16,
|
|
1852
|
+
card: 0.22,
|
|
1853
|
+
popover: 0.22,
|
|
1854
|
+
muted: 0.27,
|
|
1855
|
+
border: 0.4,
|
|
1856
|
+
input: 0.4,
|
|
1857
|
+
"muted-foreground": 0.72,
|
|
1858
|
+
foreground: 0.97
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
function deriveNeutrals(neutral, mode) {
|
|
1862
|
+
if (neutral === "kinetix") return {};
|
|
1863
|
+
const { h, c } = NEUTRAL_TINT[neutral];
|
|
1864
|
+
const ladder = NEUTRAL_LADDER[mode];
|
|
1865
|
+
const at = (l) => oklchToHex({ l, c, h });
|
|
1866
|
+
const out = {};
|
|
1867
|
+
for (const [token, l] of Object.entries(ladder)) out[token] = at(l);
|
|
1868
|
+
out["card-foreground"] = out.foreground;
|
|
1869
|
+
out["popover-foreground"] = out.foreground;
|
|
1870
|
+
return out;
|
|
1871
|
+
}
|
|
1872
|
+
var RADIUS_STEPS = {
|
|
1873
|
+
square: { sm: 0, md: 0, lg: 0, xl: 0 },
|
|
1874
|
+
small: { sm: 4, md: 4, lg: 8, xl: 12 },
|
|
1875
|
+
default: { sm: 4, md: 8, lg: 12, xl: 16 },
|
|
1876
|
+
rounded: { sm: 8, md: 12, lg: 16, xl: 24 },
|
|
1877
|
+
soft: { sm: 12, md: 20, lg: 28, xl: 36 }
|
|
1878
|
+
};
|
|
1879
|
+
function deriveRadius(radius) {
|
|
1880
|
+
return { ...RADIUS_STEPS[radius] };
|
|
1881
|
+
}
|
|
1882
|
+
function deriveElevation(surface) {
|
|
1883
|
+
switch (surface) {
|
|
1884
|
+
case "soft":
|
|
1885
|
+
return { ...SHIPPED_ELEVATION };
|
|
1886
|
+
case "elevated":
|
|
1887
|
+
return {
|
|
1888
|
+
sm: SHIPPED_ELEVATION.md,
|
|
1889
|
+
md: SHIPPED_ELEVATION.lg,
|
|
1890
|
+
lg: SHIPPED_ELEVATION.xl,
|
|
1891
|
+
xl: SHIPPED_ELEVATION.xl
|
|
1892
|
+
};
|
|
1893
|
+
case "flat":
|
|
1894
|
+
case "bordered":
|
|
1895
|
+
return { sm: [], md: [], lg: [], xl: [] };
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
function deriveSurfaceBorder(surface, mode, borderHex) {
|
|
1899
|
+
if (surface !== "bordered") return null;
|
|
1900
|
+
const border = hexToOklch(borderHex);
|
|
1901
|
+
return border ? oklchToHex(adjust(border, { dl: mode === "light" ? -0.14 : 0.14 })) : null;
|
|
1902
|
+
}
|
|
1903
|
+
var CHART_HUES = {
|
|
1904
|
+
categorical: [255, 60, 145, 330, 200],
|
|
1905
|
+
cool: [200, 250, 290, 170, 225],
|
|
1906
|
+
// A warm band is only ~100° wide, so the hues are ordered against the lightness ladder below rather
|
|
1907
|
+
// than run in sequence: the two closest hues (45 and 55) sit on the two most separated lightnesses.
|
|
1908
|
+
// Running them in order gave two oranges a reader could not tell apart.
|
|
1909
|
+
warm: [20, 55, 45, 115, 90]
|
|
1910
|
+
};
|
|
1911
|
+
var CHART_L = {
|
|
1912
|
+
light: [0.55, 0.68, 0.46, 0.62, 0.74],
|
|
1913
|
+
dark: [0.7, 0.62, 0.78, 0.55, 0.85]
|
|
1914
|
+
};
|
|
1915
|
+
function deriveChart(palette, brandHex, mode) {
|
|
1916
|
+
if (palette === "kinetix") return {};
|
|
1917
|
+
const hues = palette === "brand" ? (() => {
|
|
1918
|
+
const h = hexToOklch(brandHex)?.h ?? 255;
|
|
1919
|
+
return [0, 50, -50, 100, -100].map((d) => ((h + d) % 360 + 360) % 360);
|
|
1920
|
+
})() : CHART_HUES[palette];
|
|
1921
|
+
const ls = CHART_L[mode];
|
|
1922
|
+
const out = {};
|
|
1923
|
+
hues.forEach((h, i) => {
|
|
1924
|
+
out[`chart-${i + 1}`] = oklchToHex({ l: ls[i], c: Math.min(0.16, MAX_CHROMA), h });
|
|
1925
|
+
});
|
|
1926
|
+
return out;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// ../create-theme/src/resolve.ts
|
|
1930
|
+
var ALIASED_FROM = {
|
|
1931
|
+
"card-foreground": "foreground",
|
|
1932
|
+
"popover-foreground": "foreground",
|
|
1933
|
+
input: "border"
|
|
1934
|
+
};
|
|
1935
|
+
var FOREGROUND_OF = Object.fromEntries(CONTRAST_PAIRS);
|
|
1936
|
+
function shippedColorsFor(mode) {
|
|
1937
|
+
const out = {};
|
|
1938
|
+
for (const row2 of TOKEN_CONTRACT) out[row2.token] = mode === "dark" ? row2.dark : row2.light;
|
|
1939
|
+
for (const [token, source] of Object.entries(ALIASED_FROM)) {
|
|
1940
|
+
const value = out[source];
|
|
1941
|
+
if (value) out[token] = value;
|
|
1942
|
+
}
|
|
1943
|
+
for (const [base, fg] of Object.entries(FOREGROUND_OF)) {
|
|
1944
|
+
const baseHex = out[base];
|
|
1945
|
+
if (baseHex && !out[fg]) out[fg] = foregroundFor(baseHex);
|
|
1946
|
+
}
|
|
1947
|
+
return out;
|
|
1948
|
+
}
|
|
1949
|
+
var SHIPPED_COLORS = {
|
|
1950
|
+
light: shippedColorsFor("light"),
|
|
1951
|
+
dark: shippedColorsFor("dark")
|
|
1952
|
+
};
|
|
1953
|
+
var SHIPPED_THEME = {
|
|
1954
|
+
light: { colors: SHIPPED_COLORS.light, radius: SHIPPED_RADIUS, elevation: SHIPPED_ELEVATION },
|
|
1955
|
+
dark: { colors: SHIPPED_COLORS.dark, radius: SHIPPED_RADIUS, elevation: SHIPPED_ELEVATION }
|
|
1956
|
+
};
|
|
1957
|
+
function resolveMode(design, mode) {
|
|
1958
|
+
const colors = { ...SHIPPED_COLORS[mode] };
|
|
1959
|
+
Object.assign(colors, deriveNeutrals(design.neutral, mode));
|
|
1960
|
+
if (design.brand.toLowerCase() !== DEFAULT_PRESET.brand) {
|
|
1961
|
+
Object.assign(colors, deriveBrandRoles(design.brand, mode));
|
|
1962
|
+
}
|
|
1963
|
+
Object.assign(colors, deriveChart(design.chartPalette, design.brand, mode));
|
|
1964
|
+
const border = deriveSurfaceBorder(design.surface, mode, colors.border ?? "#000000");
|
|
1965
|
+
if (border) colors.border = border;
|
|
1966
|
+
for (const [token, hex] of Object.entries(design.manualOverrides)) {
|
|
1967
|
+
if (hex) colors[token] = hex;
|
|
1968
|
+
}
|
|
1969
|
+
for (const [base, fg] of Object.entries(FOREGROUND_OF)) {
|
|
1970
|
+
if (design.manualOverrides[base] && !design.manualOverrides[fg]) {
|
|
1971
|
+
colors[fg] = foregroundFor(colors[base]);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
return {
|
|
1975
|
+
colors,
|
|
1976
|
+
radius: deriveRadius(design.radius),
|
|
1977
|
+
elevation: deriveElevation(design.surface)
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
function canonicalDesign(design) {
|
|
1981
|
+
const result = validate(canonicalize(design));
|
|
1982
|
+
return result.ok ? result.config : design;
|
|
1983
|
+
}
|
|
1984
|
+
function resolveCreateTheme(input) {
|
|
1985
|
+
const design = canonicalDesign(input);
|
|
1986
|
+
return {
|
|
1987
|
+
light: resolveMode(design, "light"),
|
|
1988
|
+
dark: resolveMode(design, "dark"),
|
|
1989
|
+
meta: {
|
|
1990
|
+
design,
|
|
1991
|
+
// In token order, not insertion order. `Object.keys` would hand back whatever order the overrides
|
|
1992
|
+
// happened to be written in, so a decoded preset (canonicalized, sorted) and the design it was
|
|
1993
|
+
// encoded from would resolve to two theme objects that are not equal — which breaks the one
|
|
1994
|
+
// property the whole pipeline rests on. Token order, rather than alphabetical, because that is the
|
|
1995
|
+
// order the raw editor and the CLI already list overrides in.
|
|
1996
|
+
manualTokens: ACCEPTED_TOKENS.filter((token) => design.manualOverrides[token]),
|
|
1997
|
+
isDefault: isDefaultPreset(design)
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// ../create-theme/src/exporters/css.ts
|
|
2003
|
+
var px2 = (value) => Number(value) === 0 ? "0" : `${value}px`;
|
|
2004
|
+
function shadowCss(layers) {
|
|
2005
|
+
if (layers.length === 0) return "none";
|
|
2006
|
+
return layers.map((l) => `${px2(l.offsetX)} ${px2(l.offsetY)} ${px2(l.blur)} ${px2(l.spread)} ${l.color}`).join(", ");
|
|
2007
|
+
}
|
|
2008
|
+
var declaration = (token, value) => ` --${token}: ${value};`;
|
|
2009
|
+
var block = (selector, lines) => lines.length ? `${selector} {
|
|
2010
|
+
${lines.join("\n")}
|
|
2011
|
+
}` : "";
|
|
2012
|
+
function colorLines(resolved, base) {
|
|
2013
|
+
return Object.keys(resolved.colors).filter((token) => {
|
|
2014
|
+
const value = resolved.colors[token];
|
|
2015
|
+
return value && value.toLowerCase() !== base.colors[token]?.toLowerCase();
|
|
2016
|
+
}).sort().map((token) => declaration(token, hexToHslChannels(resolved.colors[token])));
|
|
2017
|
+
}
|
|
2018
|
+
function cssVarOverrides(resolved, base = SHIPPED_THEME.light) {
|
|
2019
|
+
const out = {};
|
|
2020
|
+
for (const step of RADIUS_STEP_NAMES) {
|
|
2021
|
+
if (resolved.radius[step] !== base.radius[step]) out[`radius-${step}`] = `${resolved.radius[step]}px`;
|
|
2022
|
+
}
|
|
2023
|
+
for (const step of ELEVATION_STEP_NAMES) {
|
|
2024
|
+
const value = shadowCss(resolved.elevation[step]);
|
|
2025
|
+
if (value !== shadowCss(base.elevation[step])) out[`shadow-${step}`] = value;
|
|
2026
|
+
}
|
|
2027
|
+
return out;
|
|
2028
|
+
}
|
|
2029
|
+
var varLines = (resolved, base) => Object.entries(cssVarOverrides(resolved, base)).map(([token, value]) => declaration(token, value));
|
|
2030
|
+
function exportCss(theme2, options = {}) {
|
|
2031
|
+
const rootSelector = options.rootSelector ?? ":root";
|
|
2032
|
+
const darkSelector = options.darkSelector ?? ".dark";
|
|
2033
|
+
const rootVars = varLines(theme2.light, SHIPPED_THEME.light);
|
|
2034
|
+
const root = [...colorLines(theme2.light, SHIPPED_THEME.light), ...rootVars];
|
|
2035
|
+
const dark = [
|
|
2036
|
+
...colorLines(theme2.dark, SHIPPED_THEME.dark),
|
|
2037
|
+
...varLines(theme2.dark, SHIPPED_THEME.dark).filter((line) => !rootVars.includes(line))
|
|
2038
|
+
];
|
|
2039
|
+
return [block(rootSelector, root), block(darkSelector, dark)].filter(Boolean).join("\n\n");
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// ../create-theme/src/exporters/flutter.ts
|
|
2043
|
+
var DEFAULT_FLUTTER_SYMBOL = "CreateTheme";
|
|
2044
|
+
var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
2045
|
+
"assert",
|
|
2046
|
+
"break",
|
|
2047
|
+
"case",
|
|
2048
|
+
"catch",
|
|
2049
|
+
"class",
|
|
2050
|
+
"const",
|
|
2051
|
+
"continue",
|
|
2052
|
+
"default",
|
|
2053
|
+
"do",
|
|
2054
|
+
"else",
|
|
2055
|
+
"enum",
|
|
2056
|
+
"extends",
|
|
2057
|
+
"false",
|
|
2058
|
+
"final",
|
|
2059
|
+
"finally",
|
|
2060
|
+
"for",
|
|
2061
|
+
"if",
|
|
2062
|
+
"in",
|
|
2063
|
+
"is",
|
|
2064
|
+
"new",
|
|
2065
|
+
"null",
|
|
2066
|
+
"rethrow",
|
|
2067
|
+
"return",
|
|
2068
|
+
"super",
|
|
2069
|
+
"switch",
|
|
2070
|
+
"this",
|
|
2071
|
+
"throw",
|
|
2072
|
+
"true",
|
|
2073
|
+
"try",
|
|
2074
|
+
"var",
|
|
2075
|
+
"void",
|
|
2076
|
+
"while",
|
|
2077
|
+
"with"
|
|
2078
|
+
]);
|
|
2079
|
+
function dartSymbolError(symbol) {
|
|
2080
|
+
if (symbol.length === 0) return "A theme name cannot be empty.";
|
|
2081
|
+
if (symbol.length > 64) return "A theme name must be 64 characters or fewer.";
|
|
2082
|
+
if (/[\p{Cc}\p{Cf}]/u.test(symbol)) return "A theme name cannot contain control characters.";
|
|
2083
|
+
if (/^[0-9]/.test(symbol)) return `"${symbol}" cannot start with a digit \u2014 Dart type names begin with a letter or underscore.`;
|
|
2084
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(symbol)) {
|
|
2085
|
+
return `"${symbol}" is not a Dart identifier \u2014 use letters, digits and underscores only.`;
|
|
2086
|
+
}
|
|
2087
|
+
if (DART_RESERVED_WORDS.has(symbol)) return `"${symbol}" is a Dart reserved word and cannot be a type name.`;
|
|
2088
|
+
return null;
|
|
2089
|
+
}
|
|
2090
|
+
function dartColor(hex) {
|
|
2091
|
+
const h = hex.replace("#", "").trim();
|
|
2092
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
|
|
2093
|
+
return `Color(0xFF${h.toUpperCase()})`;
|
|
2094
|
+
}
|
|
2095
|
+
var FLUTTER_COLOR_FIELDS = [
|
|
2096
|
+
["primary", "primary"],
|
|
2097
|
+
["primaryForeground", "primary-foreground"],
|
|
2098
|
+
["action", "action"],
|
|
2099
|
+
["actionForeground", "action-foreground"],
|
|
2100
|
+
["actionHover", "action-hover"],
|
|
2101
|
+
["actionPressed", "action-pressed"],
|
|
2102
|
+
["link", "link"],
|
|
2103
|
+
["focus", "focus"],
|
|
2104
|
+
["brand", "brand"],
|
|
2105
|
+
["brandForeground", "brand-foreground"],
|
|
2106
|
+
["secondary", "secondary"],
|
|
2107
|
+
["secondaryForeground", "secondary-foreground"],
|
|
2108
|
+
["destructive", "destructive"],
|
|
2109
|
+
["destructiveForeground", "destructive-foreground"],
|
|
2110
|
+
["foreground", "foreground"],
|
|
2111
|
+
["background", "background"],
|
|
2112
|
+
["border", "border"],
|
|
2113
|
+
["input", "input"],
|
|
2114
|
+
["ring", "ring"],
|
|
2115
|
+
["muted", "muted"],
|
|
2116
|
+
["mutedForeground", "muted-foreground"],
|
|
2117
|
+
["accent", "accent"],
|
|
2118
|
+
["accentForeground", "accent-foreground"],
|
|
2119
|
+
["tertiary", "tertiary"],
|
|
2120
|
+
["tertiaryForeground", "tertiary-foreground"],
|
|
2121
|
+
["warning", "warning"],
|
|
2122
|
+
["warningForeground", "warning-foreground"],
|
|
2123
|
+
["card", "card"],
|
|
2124
|
+
["cardForeground", "card-foreground"],
|
|
2125
|
+
["success", "success"],
|
|
2126
|
+
["successForeground", "success-foreground"],
|
|
2127
|
+
["info", "info"],
|
|
2128
|
+
["infoForeground", "info-foreground"],
|
|
2129
|
+
["popover", "popover"],
|
|
2130
|
+
["popoverForeground", "popover-foreground"]
|
|
2131
|
+
];
|
|
2132
|
+
var FLUTTER_CHART_STOPS = [1, 2, 3, 4, 5];
|
|
2133
|
+
var shippedRef = (appearance, field) => `KinetixColors.${appearance}.${field}`;
|
|
2134
|
+
function fieldValue(mode, appearance, field, token) {
|
|
2135
|
+
const resolved = mode.colors[token];
|
|
2136
|
+
if (!resolved) return shippedRef(appearance, field);
|
|
2137
|
+
const shipped = SHIPPED_COLORS[appearance][token];
|
|
2138
|
+
if (shipped && shipped.toLowerCase() === resolved.toLowerCase()) return shippedRef(appearance, field);
|
|
2139
|
+
return dartColor(resolved) ?? shippedRef(appearance, field);
|
|
2140
|
+
}
|
|
2141
|
+
function colorSet(mode, appearance) {
|
|
2142
|
+
const fields = FLUTTER_COLOR_FIELDS.map(
|
|
2143
|
+
([field, token]) => ` ${field}: ${fieldValue(mode, appearance, field, token)},`
|
|
2144
|
+
);
|
|
2145
|
+
const chart = FLUTTER_CHART_STOPS.map(
|
|
2146
|
+
(stop) => ` ${fieldValue(mode, appearance, `chart${stop}`, `chart-${stop}`)},`
|
|
2147
|
+
);
|
|
2148
|
+
return [...fields, " chart: <Color>[", ...chart, " ],"].join("\n");
|
|
2149
|
+
}
|
|
2150
|
+
function header(symbol, theme2) {
|
|
2151
|
+
const design = theme2.meta.design;
|
|
2152
|
+
return [
|
|
2153
|
+
"//",
|
|
2154
|
+
`// ${symbol} \u2014 a KinetixUI Create theme.`,
|
|
2155
|
+
"//",
|
|
2156
|
+
"// Generated by `kinetixui preset flutter`. Edit the design in Create and re-export rather than",
|
|
2157
|
+
"// editing this file: it is a rendering of a preset, not a source of one.",
|
|
2158
|
+
"//",
|
|
2159
|
+
`// Theme colour ${design.brand} \xB7 ${design.neutral} neutral \xB7 ${design.chartPalette} charts`,
|
|
2160
|
+
"//",
|
|
2161
|
+
"// Every semantic colour KinetixColors declares is carried here, light and dark \u2014 Flutter's contract",
|
|
2162
|
+
"// is the complete one, so no role is left behind.",
|
|
2163
|
+
"//",
|
|
2164
|
+
"// COLOURS ONLY. Radius and elevation are not themeable at runtime: widgets read `KinetixRadius`",
|
|
2165
|
+
"// constants directly and `KinetixTheme` carries neither them nor `KinetixShadows`, so this design's",
|
|
2166
|
+
`// radius (${design.radius}) and surface treatment (${design.surface}) have nowhere to be installed.`,
|
|
2167
|
+
"// They apply on the web. That is the only thing this file does not carry.",
|
|
2168
|
+
"//",
|
|
2169
|
+
"// A field written as `KinetixColors.light.\u2026` is one this design did not change, and it keeps",
|
|
2170
|
+
"// following the library. Six of them \u2014 tertiary, tertiaryForeground, info, infoForeground,",
|
|
2171
|
+
"// successForeground and warningForeground \u2014 are always references, because Create does not model",
|
|
2172
|
+
"// those tokens.",
|
|
2173
|
+
"//",
|
|
2174
|
+
"// These are `static final` rather than `static const`: Dart's constant expressions do not include",
|
|
2175
|
+
"// instance field access, so a `static const` could not reference the shipped palette. The theme is",
|
|
2176
|
+
"// still usable everywhere a const one would be \u2014 `KinetixTheme.custom` takes it fine \u2014 you just",
|
|
2177
|
+
"// cannot write `const KinetixTheme.custom(\u2026)` around it.",
|
|
2178
|
+
"//",
|
|
2179
|
+
"// Apply it to Kinetix widgets:",
|
|
2180
|
+
"//",
|
|
2181
|
+
`// KinetixTheme.custom(light: ${symbol}.light, dark: ${symbol}.dark, child: App())`,
|
|
2182
|
+
"//",
|
|
2183
|
+
"// \u2026and to Material or Cupertino widgets, whose own theme APIs represent the subset they can:",
|
|
2184
|
+
"//",
|
|
2185
|
+
`// MaterialApp(theme: KinetixMaterialTheme.fromColors(Brightness.light, ${symbol}.light))`,
|
|
2186
|
+
"//"
|
|
2187
|
+
].join("\n");
|
|
2188
|
+
}
|
|
2189
|
+
function exportFlutter(theme2, options = {}) {
|
|
2190
|
+
const symbol = options.symbol ?? DEFAULT_FLUTTER_SYMBOL;
|
|
2191
|
+
const reason = dartSymbolError(symbol);
|
|
2192
|
+
if (reason) throw new Error(reason);
|
|
2193
|
+
return [
|
|
2194
|
+
header(symbol, theme2),
|
|
2195
|
+
"",
|
|
2196
|
+
"import 'package:flutter/widgets.dart';",
|
|
2197
|
+
"import 'package:kinetix_ui/kinetix_ui.dart';",
|
|
2198
|
+
"",
|
|
2199
|
+
// `abstract final class` is Dart 3 and the package floor is >=3.6, so it is available. It is the
|
|
2200
|
+
// idiomatic way to say "namespace, never instantiated, never extended" — the same intent SwiftUI's
|
|
2201
|
+
// `enum` and Compose's `object` express.
|
|
2202
|
+
`abstract final class ${symbol} {`,
|
|
2203
|
+
" static final KinetixColors light = KinetixColors(",
|
|
2204
|
+
colorSet(theme2.light, "light"),
|
|
2205
|
+
" );",
|
|
2206
|
+
"",
|
|
2207
|
+
" static final KinetixColors dark = KinetixColors(",
|
|
2208
|
+
colorSet(theme2.dark, "dark"),
|
|
2209
|
+
" );",
|
|
2210
|
+
"}",
|
|
2211
|
+
""
|
|
2212
|
+
].join("\n");
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// ../create-theme/src/exporters/compose.ts
|
|
2216
|
+
var DEFAULT_COMPOSE_SYMBOL = "CreateTheme";
|
|
2217
|
+
var KOTLIN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2218
|
+
"as",
|
|
2219
|
+
"break",
|
|
2220
|
+
"class",
|
|
2221
|
+
"continue",
|
|
2222
|
+
"do",
|
|
2223
|
+
"else",
|
|
2224
|
+
"false",
|
|
2225
|
+
"for",
|
|
2226
|
+
"fun",
|
|
2227
|
+
"if",
|
|
2228
|
+
"in",
|
|
2229
|
+
"interface",
|
|
2230
|
+
"is",
|
|
2231
|
+
"null",
|
|
2232
|
+
"object",
|
|
2233
|
+
"package",
|
|
2234
|
+
"return",
|
|
2235
|
+
"super",
|
|
2236
|
+
"this",
|
|
2237
|
+
"throw",
|
|
2238
|
+
"true",
|
|
2239
|
+
"try",
|
|
2240
|
+
"typealias",
|
|
2241
|
+
"typeof",
|
|
2242
|
+
"val",
|
|
2243
|
+
"var",
|
|
2244
|
+
"when",
|
|
2245
|
+
"while"
|
|
2246
|
+
]);
|
|
2247
|
+
function kotlinSymbolError(symbol) {
|
|
2248
|
+
if (symbol.length === 0) return "A theme name cannot be empty.";
|
|
2249
|
+
if (symbol.length > 64) return "A theme name must be 64 characters or fewer.";
|
|
2250
|
+
if (/[\p{Cc}\p{Cf}]/u.test(symbol)) return "A theme name cannot contain control characters.";
|
|
2251
|
+
if (/^[0-9]/.test(symbol)) return `"${symbol}" cannot start with a digit \u2014 Kotlin type names begin with a letter or underscore.`;
|
|
2252
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(symbol)) {
|
|
2253
|
+
return `"${symbol}" is not a Kotlin identifier \u2014 use letters, digits and underscores only.`;
|
|
2254
|
+
}
|
|
2255
|
+
if (KOTLIN_KEYWORDS.has(symbol)) return `"${symbol}" is a Kotlin keyword and cannot be a type name.`;
|
|
2256
|
+
return null;
|
|
2257
|
+
}
|
|
2258
|
+
function kotlinFieldName(token) {
|
|
2259
|
+
return token.replace(/[-_]([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2260
|
+
}
|
|
2261
|
+
function composeColor(hex) {
|
|
2262
|
+
const h = hex.replace("#", "").trim();
|
|
2263
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
|
|
2264
|
+
return `Color(0xff${h.toLowerCase()})`;
|
|
2265
|
+
}
|
|
2266
|
+
var COMPOSE_COLOR_FIELDS = [
|
|
2267
|
+
["primary", "primary"],
|
|
2268
|
+
["primaryForeground", "primary-foreground"],
|
|
2269
|
+
["action", "action"],
|
|
2270
|
+
["actionForeground", "action-foreground"],
|
|
2271
|
+
["actionHover", "action-hover"],
|
|
2272
|
+
["actionPressed", "action-pressed"],
|
|
2273
|
+
["link", "link"],
|
|
2274
|
+
["focus", "focus"],
|
|
2275
|
+
["brand", "brand"],
|
|
2276
|
+
["brandForeground", "brand-foreground"],
|
|
2277
|
+
["secondary", "secondary"],
|
|
2278
|
+
["secondaryForeground", "secondary-foreground"],
|
|
2279
|
+
["destructive", "destructive"],
|
|
2280
|
+
["destructiveForeground", "destructive-foreground"],
|
|
2281
|
+
["foreground", "foreground"],
|
|
2282
|
+
["background", "background"],
|
|
2283
|
+
["border", "border"],
|
|
2284
|
+
["muted", "muted"],
|
|
2285
|
+
["mutedForeground", "muted-foreground"],
|
|
2286
|
+
["accent", "accent"],
|
|
2287
|
+
["accentForeground", "accent-foreground"],
|
|
2288
|
+
["tertiary", "tertiary"],
|
|
2289
|
+
["warning", "warning"],
|
|
2290
|
+
["warningForeground", "warning-foreground"],
|
|
2291
|
+
["card", "card"],
|
|
2292
|
+
["cardForeground", "card-foreground"],
|
|
2293
|
+
["success", "success"],
|
|
2294
|
+
["successForeground", "success-foreground"],
|
|
2295
|
+
["info", "info"],
|
|
2296
|
+
["infoForeground", "info-foreground"],
|
|
2297
|
+
["popover", "popover"],
|
|
2298
|
+
["popoverForeground", "popover-foreground"]
|
|
2299
|
+
];
|
|
2300
|
+
var COMPOSE_CHART_STOPS = [1, 2, 3, 4, 5];
|
|
2301
|
+
var COMPOSE_UNMAPPED_PRESET_ROLES = ["input", "ring"];
|
|
2302
|
+
var COMPOSE_NATIVE_THEME_GAPS = ["tertiary-foreground"];
|
|
2303
|
+
var SHIPPED_OBJECT = { light: "KinetixTokensLight", dark: "KinetixTokensDark" };
|
|
2304
|
+
var shippedRef2 = (appearance, token) => `${SHIPPED_OBJECT[appearance]}.color${kotlinFieldName(token).replace(/^./, (c) => c.toUpperCase())}`;
|
|
2305
|
+
function fieldValue2(mode, appearance, token) {
|
|
2306
|
+
const resolved = mode.colors[token];
|
|
2307
|
+
if (!resolved) return shippedRef2(appearance, token);
|
|
2308
|
+
const shipped = SHIPPED_COLORS[appearance][token];
|
|
2309
|
+
if (shipped && shipped.toLowerCase() === resolved.toLowerCase()) return shippedRef2(appearance, token);
|
|
2310
|
+
return composeColor(resolved) ?? shippedRef2(appearance, token);
|
|
2311
|
+
}
|
|
2312
|
+
function colorSet2(mode, appearance) {
|
|
2313
|
+
const fields = COMPOSE_COLOR_FIELDS.map(
|
|
2314
|
+
([field, token]) => ` ${field} = ${fieldValue2(mode, appearance, token)},`
|
|
2315
|
+
);
|
|
2316
|
+
const chart = COMPOSE_CHART_STOPS.map(
|
|
2317
|
+
(stop) => ` ${fieldValue2(mode, appearance, `chart-${stop}`)},`
|
|
2318
|
+
);
|
|
2319
|
+
return [...fields, " chart = listOf(", ...chart, " ),"].join("\n");
|
|
2320
|
+
}
|
|
2321
|
+
function header2(symbol, theme2) {
|
|
2322
|
+
const design = theme2.meta.design;
|
|
2323
|
+
return [
|
|
2324
|
+
"//",
|
|
2325
|
+
`// ${symbol} \u2014 a KinetixUI Create theme.`,
|
|
2326
|
+
"//",
|
|
2327
|
+
"// Generated by `kinetixui preset compose`. Edit the design in Create and re-export rather than",
|
|
2328
|
+
"// editing this file: it is a rendering of a preset, not a source of one.",
|
|
2329
|
+
"//",
|
|
2330
|
+
`// Theme colour ${design.brand} \xB7 ${design.neutral} neutral \xB7 ${design.chartPalette} charts`,
|
|
2331
|
+
"//",
|
|
2332
|
+
"// COLOURS ONLY. KinetixUI for Compose is themeable through `KinetixColors`, and this file carries",
|
|
2333
|
+
"// every colour it has a field for. What this design can express and Compose cannot receive:",
|
|
2334
|
+
"//",
|
|
2335
|
+
`// - radius (${design.radius}) and surface treatment (${design.surface}). Compose reads these from`,
|
|
2336
|
+
"// generated constants, not from a runtime theme, so nothing written here could override them.",
|
|
2337
|
+
"// Both apply on the web.",
|
|
2338
|
+
`// - \`${COMPOSE_UNMAPPED_PRESET_ROLES.join("` and `")}\`. Create resolves both, and a preset can override either by`,
|
|
2339
|
+
"// hand, but `KinetixColors` has no field for them.",
|
|
2340
|
+
"//",
|
|
2341
|
+
"// That is the complete list for this design. Nothing else was dropped.",
|
|
2342
|
+
"//",
|
|
2343
|
+
`// Separately, and not a Create limitation: \`${COMPOSE_NATIVE_THEME_GAPS.join("`, `")}\` is in the Kinetix`,
|
|
2344
|
+
"// token contract and in the SwiftUI theme, but Compose's `KinetixColors` does not expose it. Create",
|
|
2345
|
+
"// does not model that token at all, so no preset can set it and nothing here is affected by it.",
|
|
2346
|
+
"//",
|
|
2347
|
+
"// A field written as `KinetixTokensLight.\u2026` is one this design did not change, and it keeps",
|
|
2348
|
+
"// following the library.",
|
|
2349
|
+
"//"
|
|
2350
|
+
].join("\n");
|
|
2351
|
+
}
|
|
2352
|
+
function exportCompose(theme2, options = {}) {
|
|
2353
|
+
const symbol = options.symbol ?? DEFAULT_COMPOSE_SYMBOL;
|
|
2354
|
+
const reason = kotlinSymbolError(symbol);
|
|
2355
|
+
if (reason) throw new Error(reason);
|
|
2356
|
+
const light = colorSet2(theme2.light, "light");
|
|
2357
|
+
const dark = colorSet2(theme2.dark, "dark");
|
|
2358
|
+
const usesColor = `${light}${dark}`.includes("Color(0x");
|
|
2359
|
+
return [
|
|
2360
|
+
header2(symbol, theme2),
|
|
2361
|
+
"",
|
|
2362
|
+
"package com.kinetixui.create",
|
|
2363
|
+
"",
|
|
2364
|
+
// Alphabetical, and aliased exactly as the package's own Theme.kt does it:
|
|
2365
|
+
// `com.kinetixui.tokens.KinetixTheme` is the generated token object while `com.kinetixui.ui.KinetixTheme`
|
|
2366
|
+
// is the composable, so the bare name is ambiguous in any file that wants both.
|
|
2367
|
+
...usesColor ? ["import androidx.compose.ui.graphics.Color"] : [],
|
|
2368
|
+
"import com.kinetixui.tokens.KinetixTheme as KinetixTokensLight",
|
|
2369
|
+
"import com.kinetixui.tokens.KinetixThemeDark as KinetixTokensDark",
|
|
2370
|
+
"import com.kinetixui.ui.KinetixColors",
|
|
2371
|
+
"",
|
|
2372
|
+
`object ${symbol} {`,
|
|
2373
|
+
" /** Apply with `KinetixTheme(light = light, dark = dark) { \u2026 }`. */",
|
|
2374
|
+
" val light = KinetixColors(",
|
|
2375
|
+
light,
|
|
2376
|
+
" )",
|
|
2377
|
+
"",
|
|
2378
|
+
" val dark = KinetixColors(",
|
|
2379
|
+
dark,
|
|
2380
|
+
" )",
|
|
2381
|
+
"}",
|
|
2382
|
+
""
|
|
2383
|
+
].join("\n");
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// ../create-theme/src/exporters/swiftui.ts
|
|
2387
|
+
var DEFAULT_SWIFT_SYMBOL = "CreateTheme";
|
|
2388
|
+
var SWIFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2389
|
+
"associatedtype",
|
|
2390
|
+
"borrowing",
|
|
2391
|
+
"class",
|
|
2392
|
+
"consuming",
|
|
2393
|
+
"deinit",
|
|
2394
|
+
"enum",
|
|
2395
|
+
"extension",
|
|
2396
|
+
"fileprivate",
|
|
2397
|
+
"func",
|
|
2398
|
+
"import",
|
|
2399
|
+
"init",
|
|
2400
|
+
"inout",
|
|
2401
|
+
"internal",
|
|
2402
|
+
"let",
|
|
2403
|
+
"macro",
|
|
2404
|
+
"nonisolated",
|
|
2405
|
+
"open",
|
|
2406
|
+
"operator",
|
|
2407
|
+
"private",
|
|
2408
|
+
"precedencegroup",
|
|
2409
|
+
"protocol",
|
|
2410
|
+
"public",
|
|
2411
|
+
"rethrows",
|
|
2412
|
+
"static",
|
|
2413
|
+
"struct",
|
|
2414
|
+
"subscript",
|
|
2415
|
+
"typealias",
|
|
2416
|
+
"var",
|
|
2417
|
+
"break",
|
|
2418
|
+
"case",
|
|
2419
|
+
"catch",
|
|
2420
|
+
"continue",
|
|
2421
|
+
"default",
|
|
2422
|
+
"defer",
|
|
2423
|
+
"do",
|
|
2424
|
+
"else",
|
|
2425
|
+
"fallthrough",
|
|
2426
|
+
"for",
|
|
2427
|
+
"guard",
|
|
2428
|
+
"if",
|
|
2429
|
+
"in",
|
|
2430
|
+
"repeat",
|
|
2431
|
+
"return",
|
|
2432
|
+
"throw",
|
|
2433
|
+
"switch",
|
|
2434
|
+
"where",
|
|
2435
|
+
"while",
|
|
2436
|
+
"Any",
|
|
2437
|
+
"as",
|
|
2438
|
+
"await",
|
|
2439
|
+
"false",
|
|
2440
|
+
"is",
|
|
2441
|
+
"nil",
|
|
2442
|
+
"rethrows",
|
|
2443
|
+
"self",
|
|
2444
|
+
"Self",
|
|
2445
|
+
"super",
|
|
2446
|
+
"throw",
|
|
2447
|
+
"throws",
|
|
2448
|
+
"true",
|
|
2449
|
+
"try"
|
|
2450
|
+
]);
|
|
2451
|
+
function swiftSymbolError(symbol) {
|
|
2452
|
+
if (symbol.length === 0) return "A theme name cannot be empty.";
|
|
2453
|
+
if (symbol.length > 64) return "A theme name must be 64 characters or fewer.";
|
|
2454
|
+
if (/[\p{Cc}\p{Cf}]/u.test(symbol)) return "A theme name cannot contain control characters.";
|
|
2455
|
+
if (/^[0-9]/.test(symbol)) return `"${symbol}" cannot start with a digit \u2014 Swift type names begin with a letter or underscore.`;
|
|
2456
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(symbol)) {
|
|
2457
|
+
return `"${symbol}" is not a Swift identifier \u2014 use letters, digits and underscores only.`;
|
|
2458
|
+
}
|
|
2459
|
+
if (SWIFT_KEYWORDS.has(symbol)) return `"${symbol}" is a Swift keyword and cannot be a type name.`;
|
|
2460
|
+
return null;
|
|
2461
|
+
}
|
|
2462
|
+
function swiftFieldName(token) {
|
|
2463
|
+
return token.replace(/[-_]([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2464
|
+
}
|
|
2465
|
+
function swiftColor(hex) {
|
|
2466
|
+
const h = hex.replace("#", "").trim();
|
|
2467
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
|
|
2468
|
+
const [r, g, b] = swiftUiChannels(`#${h}`);
|
|
2469
|
+
return `Color(red: ${r}, green: ${g}, blue: ${b})`;
|
|
2470
|
+
}
|
|
2471
|
+
var SWIFT_COLOR_FIELDS = [
|
|
2472
|
+
"primary",
|
|
2473
|
+
"primary-foreground",
|
|
2474
|
+
"action",
|
|
2475
|
+
"action-foreground",
|
|
2476
|
+
"action-hover",
|
|
2477
|
+
"action-pressed",
|
|
2478
|
+
"link",
|
|
2479
|
+
"focus",
|
|
2480
|
+
"brand",
|
|
2481
|
+
"brand-foreground",
|
|
2482
|
+
"secondary",
|
|
2483
|
+
"secondary-foreground",
|
|
2484
|
+
"destructive",
|
|
2485
|
+
"destructive-foreground",
|
|
2486
|
+
"foreground",
|
|
2487
|
+
"background",
|
|
2488
|
+
"border",
|
|
2489
|
+
"input",
|
|
2490
|
+
"ring",
|
|
2491
|
+
"muted",
|
|
2492
|
+
"muted-foreground",
|
|
2493
|
+
"accent",
|
|
2494
|
+
"accent-foreground",
|
|
2495
|
+
"tertiary",
|
|
2496
|
+
"tertiary-foreground",
|
|
2497
|
+
"warning",
|
|
2498
|
+
"warning-foreground",
|
|
2499
|
+
"card",
|
|
2500
|
+
"card-foreground",
|
|
2501
|
+
"success",
|
|
2502
|
+
"success-foreground",
|
|
2503
|
+
"info",
|
|
2504
|
+
"info-foreground",
|
|
2505
|
+
"popover",
|
|
2506
|
+
"popover-foreground"
|
|
2507
|
+
];
|
|
2508
|
+
var SWIFT_CHART_STOPS = [1, 2, 3, 4, 5];
|
|
2509
|
+
var SHIPPED_ENUM = { light: "KinetixColorsSwiftUI", dark: "KinetixColorsSwiftUIDark" };
|
|
2510
|
+
function fieldValue3(mode, appearance, token) {
|
|
2511
|
+
const reference = `${SHIPPED_ENUM[appearance]}.${swiftFieldName(token)}`;
|
|
2512
|
+
const resolved = mode.colors[token];
|
|
2513
|
+
if (!resolved) return reference;
|
|
2514
|
+
const shipped = SHIPPED_COLORS[appearance][token];
|
|
2515
|
+
if (shipped && shipped.toLowerCase() === resolved.toLowerCase()) return reference;
|
|
2516
|
+
return swiftColor(resolved) ?? reference;
|
|
2517
|
+
}
|
|
2518
|
+
function colorSet3(mode, appearance) {
|
|
2519
|
+
const fields = SWIFT_COLOR_FIELDS.map(
|
|
2520
|
+
(token) => ` ${swiftFieldName(token)}: ${fieldValue3(mode, appearance, token)},`
|
|
2521
|
+
);
|
|
2522
|
+
const chart = SWIFT_CHART_STOPS.map(
|
|
2523
|
+
(stop) => ` ${fieldValue3(mode, appearance, `chart-${stop}`)},`
|
|
2524
|
+
);
|
|
2525
|
+
return [...fields, " chart: [", ...chart, " ]"].join("\n");
|
|
2526
|
+
}
|
|
2527
|
+
function header3(symbol, theme2) {
|
|
2528
|
+
const design = theme2.meta.design;
|
|
2529
|
+
return [
|
|
2530
|
+
"//",
|
|
2531
|
+
`// ${symbol} \u2014 a KinetixUI Create theme.`,
|
|
2532
|
+
"//",
|
|
2533
|
+
"// Generated by `kinetixui preset swiftui`. Edit the design in Create and re-export rather than",
|
|
2534
|
+
"// editing this file: it is a rendering of a preset, not a source of one.",
|
|
2535
|
+
"//",
|
|
2536
|
+
`// Theme colour ${design.brand} \xB7 ${design.neutral} neutral \xB7 ${design.chartPalette} charts`,
|
|
2537
|
+
"//",
|
|
2538
|
+
"// COLOURS ONLY. KinetixUI for SwiftUI is themeable through `KinetixColors`; corner radius and",
|
|
2539
|
+
"// elevation are literals inside each view, with no token to override. This design's radius",
|
|
2540
|
+
`// (${design.radius}) and surface treatment (${design.surface}) therefore are NOT carried here \u2014 they`,
|
|
2541
|
+
"// apply on the web. Nothing is silently dropped; there is nowhere for them to go yet.",
|
|
2542
|
+
"//",
|
|
2543
|
+
"// A field written as `KinetixColorsSwiftUI.\u2026` is one this design did not change, and it keeps",
|
|
2544
|
+
"// following the library.",
|
|
2545
|
+
"//"
|
|
2546
|
+
].join("\n");
|
|
2547
|
+
}
|
|
2548
|
+
function exportSwiftUi(theme2, options = {}) {
|
|
2549
|
+
const symbol = options.symbol ?? DEFAULT_SWIFT_SYMBOL;
|
|
2550
|
+
const reason = swiftSymbolError(symbol);
|
|
2551
|
+
if (reason) throw new Error(reason);
|
|
2552
|
+
return [
|
|
2553
|
+
header3(symbol, theme2),
|
|
2554
|
+
"",
|
|
2555
|
+
"import SwiftUI",
|
|
2556
|
+
"import KinetixUI",
|
|
2557
|
+
"",
|
|
2558
|
+
`public enum ${symbol} {`,
|
|
2559
|
+
" /// Apply with `KinetixTheme(light: .light, dark: .dark) { \u2026 }`, or set",
|
|
2560
|
+
" /// `\\.kinetixColors` directly to pin one appearance.",
|
|
2561
|
+
` public static let light = KinetixColors(`,
|
|
2562
|
+
colorSet3(theme2.light, "light"),
|
|
2563
|
+
" )",
|
|
2564
|
+
"",
|
|
2565
|
+
` public static let dark = KinetixColors(`,
|
|
2566
|
+
colorSet3(theme2.dark, "dark"),
|
|
2567
|
+
" )",
|
|
2568
|
+
"}",
|
|
2569
|
+
""
|
|
2570
|
+
].join("\n");
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// src/commands/preset.ts
|
|
2574
|
+
function read(input) {
|
|
2575
|
+
const result = decodePreset(input);
|
|
2576
|
+
if (!result.ok) {
|
|
2577
|
+
throw new Error(result.error.message);
|
|
2578
|
+
}
|
|
2579
|
+
return result.config;
|
|
2580
|
+
}
|
|
2581
|
+
var row = (label, value) => ` ${pc8.dim(label.padEnd(16))} ${value}`;
|
|
2582
|
+
function presetDecode(input, opts) {
|
|
2583
|
+
const config = read(input);
|
|
2584
|
+
if (opts.json) {
|
|
2585
|
+
console.log(JSON.stringify(config, null, 2));
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
console.log();
|
|
2589
|
+
console.log(pc8.bold("Kinetix Create preset"));
|
|
2590
|
+
console.log();
|
|
2591
|
+
console.log(row("Theme colour", config.brand));
|
|
2592
|
+
console.log(row("Neutral", config.neutral));
|
|
2593
|
+
console.log(row("Radius", config.radius));
|
|
2594
|
+
console.log(row("Surface", config.surface));
|
|
2595
|
+
console.log(row("Chart palette", config.chartPalette));
|
|
2596
|
+
const overrides = ACCEPTED_TOKENS.filter((token) => config.manualOverrides[token]);
|
|
2597
|
+
console.log();
|
|
2598
|
+
if (overrides.length === 0) {
|
|
2599
|
+
console.log(pc8.dim(" No manual overrides \u2014 every colour is generated from the theme colour."));
|
|
2600
|
+
} else {
|
|
2601
|
+
console.log(pc8.bold(` Manual overrides (${overrides.length})`));
|
|
2602
|
+
for (const token of overrides) console.log(row(` ${token}`, config.manualOverrides[token]));
|
|
2603
|
+
}
|
|
2604
|
+
if (isDefaultPreset(config)) {
|
|
2605
|
+
console.log();
|
|
2606
|
+
console.log(pc8.dim(" This preset is the shipped Kinetix default \u2014 it changes nothing."));
|
|
2607
|
+
}
|
|
2608
|
+
console.log();
|
|
2609
|
+
console.log(
|
|
2610
|
+
pc8.dim(
|
|
2611
|
+
// Wrapped so that no command name is split across lines — a reader scanning for `theme build`
|
|
2612
|
+
// should find it, and so should a test.
|
|
2613
|
+
" A preset describes a design, not a stylesheet.\n\n kinetixui preset css <code> the same web CSS the workspace's Copy CSS produces\n kinetixui preset swiftui <code> a SwiftUI colour theme\n kinetixui preset compose <code> a Jetpack Compose colour theme\n kinetixui preset flutter <code> a Flutter colour theme\n\n For a file of literal colours, `kinetixui theme build` is unchanged."
|
|
2614
|
+
)
|
|
2615
|
+
);
|
|
2616
|
+
console.log();
|
|
2617
|
+
}
|
|
2618
|
+
async function presetCss(input, opts) {
|
|
2619
|
+
const css = exportCss(resolveCreateTheme(read(input)));
|
|
2620
|
+
if (opts.output) {
|
|
2621
|
+
await writeFile3(opts.output, css ? `${css}
|
|
2622
|
+
` : "", "utf8");
|
|
2623
|
+
console.log(pc8.green("\u2714"), css ? `Wrote ${opts.output}` : `Wrote ${opts.output} (empty \u2014 the preset is the Kinetix default)`);
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
if (!css) {
|
|
2627
|
+
console.error(pc8.dim("This preset is the shipped Kinetix default \u2014 there is nothing to override."));
|
|
2628
|
+
return;
|
|
2629
|
+
}
|
|
2630
|
+
console.log(css);
|
|
2631
|
+
}
|
|
2632
|
+
async function presetSwiftUi(input, opts) {
|
|
2633
|
+
const symbol = opts.name ?? DEFAULT_SWIFT_SYMBOL;
|
|
2634
|
+
const reason = swiftSymbolError(symbol);
|
|
2635
|
+
if (reason) throw new Error(reason);
|
|
2636
|
+
const swift = exportSwiftUi(resolveCreateTheme(read(input)), { symbol });
|
|
2637
|
+
if (opts.output) {
|
|
2638
|
+
await writeFile3(opts.output, swift, "utf8");
|
|
2639
|
+
console.log(pc8.green("\u2714"), `Wrote ${opts.output} \u2014 apply it with KinetixTheme(light: ${symbol}.light, dark: ${symbol}.dark)`);
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
process.stdout.write(swift);
|
|
2643
|
+
}
|
|
2644
|
+
async function presetCompose(input, opts) {
|
|
2645
|
+
const symbol = opts.name ?? DEFAULT_COMPOSE_SYMBOL;
|
|
2646
|
+
const reason = kotlinSymbolError(symbol);
|
|
2647
|
+
if (reason) throw new Error(reason);
|
|
2648
|
+
const kotlin = exportCompose(resolveCreateTheme(read(input)), { symbol });
|
|
2649
|
+
if (opts.output) {
|
|
2650
|
+
await writeFile3(opts.output, kotlin, "utf8");
|
|
2651
|
+
console.log(pc8.green("\u2714"), `Wrote ${opts.output} \u2014 apply it with KinetixTheme(light = ${symbol}.light, dark = ${symbol}.dark)`);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
process.stdout.write(kotlin);
|
|
2655
|
+
}
|
|
2656
|
+
async function presetFlutter(input, opts) {
|
|
2657
|
+
const symbol = opts.name ?? DEFAULT_FLUTTER_SYMBOL;
|
|
2658
|
+
const reason = dartSymbolError(symbol);
|
|
2659
|
+
if (reason) throw new Error(reason);
|
|
2660
|
+
const dart = exportFlutter(resolveCreateTheme(read(input)), { symbol });
|
|
2661
|
+
if (opts.output) {
|
|
2662
|
+
await writeFile3(opts.output, dart, "utf8");
|
|
2663
|
+
console.log(pc8.green("\u2714"), `Wrote ${opts.output} \u2014 apply it with KinetixTheme.custom(light: ${symbol}.light, dark: ${symbol}.dark, child: \u2026)`);
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
process.stdout.write(dart);
|
|
2667
|
+
}
|
|
2668
|
+
function presetUrlCommand(input, opts) {
|
|
2669
|
+
console.log(presetUrl(read(input), opts.site));
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/commands/theme.ts
|
|
2673
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2674
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2675
|
+
import path9 from "path";
|
|
2676
|
+
import pc9 from "picocolors";
|
|
2677
|
+
|
|
731
2678
|
// src/lib/theme.ts
|
|
732
|
-
var
|
|
2679
|
+
var ACCEPTED_TOKENS2 = [
|
|
733
2680
|
"background",
|
|
734
2681
|
"foreground",
|
|
735
2682
|
"card",
|
|
@@ -784,7 +2731,7 @@ var EXAMPLE_VALUES = {
|
|
|
784
2731
|
brand: "#0f172a",
|
|
785
2732
|
"brand-foreground": "#ffffff"
|
|
786
2733
|
};
|
|
787
|
-
var
|
|
2734
|
+
var FOREGROUND_OF2 = {
|
|
788
2735
|
background: "foreground",
|
|
789
2736
|
card: "card-foreground",
|
|
790
2737
|
popover: "popover-foreground",
|
|
@@ -796,7 +2743,7 @@ var FOREGROUND_OF = {
|
|
|
796
2743
|
action: "action-foreground",
|
|
797
2744
|
brand: "brand-foreground"
|
|
798
2745
|
};
|
|
799
|
-
var
|
|
2746
|
+
var CONTRAST_PAIRS2 = [
|
|
800
2747
|
["background", "foreground"],
|
|
801
2748
|
["card", "card-foreground"],
|
|
802
2749
|
["popover", "popover-foreground"],
|
|
@@ -808,7 +2755,7 @@ var CONTRAST_PAIRS = [
|
|
|
808
2755
|
["action", "action-foreground"],
|
|
809
2756
|
["brand", "brand-foreground"]
|
|
810
2757
|
];
|
|
811
|
-
var isAccepted = (t) =>
|
|
2758
|
+
var isAccepted = (t) => ACCEPTED_TOKENS2.includes(t);
|
|
812
2759
|
function parseThemeFile(text) {
|
|
813
2760
|
const values = {};
|
|
814
2761
|
const errors = [];
|
|
@@ -834,22 +2781,22 @@ function parseThemeFile(text) {
|
|
|
834
2781
|
});
|
|
835
2782
|
return { values, errors, unknownTokens };
|
|
836
2783
|
}
|
|
837
|
-
function
|
|
2784
|
+
function deriveForegrounds2(values) {
|
|
838
2785
|
const out = { ...values };
|
|
839
|
-
for (const [base, fg] of Object.entries(
|
|
2786
|
+
for (const [base, fg] of Object.entries(FOREGROUND_OF2)) {
|
|
840
2787
|
if (out[base] && !out[fg]) out[fg] = bestTextHex(out[base]);
|
|
841
2788
|
}
|
|
842
2789
|
return out;
|
|
843
2790
|
}
|
|
844
|
-
function
|
|
845
|
-
const lines =
|
|
2791
|
+
function toCssBlock2(values) {
|
|
2792
|
+
const lines = ACCEPTED_TOKENS2.filter((t) => values[t]).map((t) => ` --${t}: ${hexToHslChannels(values[t])};`);
|
|
846
2793
|
return `:root {
|
|
847
2794
|
${lines.join("\n")}
|
|
848
2795
|
}
|
|
849
2796
|
`;
|
|
850
2797
|
}
|
|
851
|
-
function
|
|
852
|
-
return
|
|
2798
|
+
function checkContrast2(values) {
|
|
2799
|
+
return CONTRAST_PAIRS2.filter(([bg, fg]) => values[bg] && values[fg]).map(([bg, fg]) => {
|
|
853
2800
|
const ratio = contrastRatio(values[bg], values[fg]);
|
|
854
2801
|
return { pair: [bg, fg], ratio, pass: ratio >= 4.5 };
|
|
855
2802
|
});
|
|
@@ -868,7 +2815,7 @@ function scaffoldThemeFile(name) {
|
|
|
868
2815
|
"# don't have to set both unless you want a specific foreground.",
|
|
869
2816
|
"#"
|
|
870
2817
|
];
|
|
871
|
-
for (const token of
|
|
2818
|
+
for (const token of ACCEPTED_TOKENS2) {
|
|
872
2819
|
lines.push(`# ${token},${EXAMPLE_VALUES[token]}`);
|
|
873
2820
|
}
|
|
874
2821
|
return lines.join("\n") + "\n";
|
|
@@ -890,10 +2837,10 @@ async function themeCreate(name, options) {
|
|
|
890
2837
|
throw new Error(`${path9.relative(cwd, file)} already exists. Pass --force to overwrite it.`);
|
|
891
2838
|
}
|
|
892
2839
|
await mkdir2(path9.dirname(file), { recursive: true });
|
|
893
|
-
await
|
|
894
|
-
console.log(
|
|
2840
|
+
await writeFile4(file, scaffoldThemeFile(name), "utf8");
|
|
2841
|
+
console.log(pc9.green("\u2714"), `Wrote ${path9.relative(cwd, file)}`);
|
|
895
2842
|
console.log(`Uncomment and edit the tokens you want to override, then run:`);
|
|
896
|
-
console.log(
|
|
2843
|
+
console.log(pc9.dim(` kinetixui theme build ${name}`));
|
|
897
2844
|
}
|
|
898
2845
|
async function themeBuild(name, options) {
|
|
899
2846
|
assertThemeName(name);
|
|
@@ -904,32 +2851,32 @@ async function themeBuild(name, options) {
|
|
|
904
2851
|
}
|
|
905
2852
|
const text = await readFile3(src, "utf8");
|
|
906
2853
|
const { values: parsed, errors, unknownTokens } = parseThemeFile(text);
|
|
907
|
-
for (const err of errors) console.log(
|
|
908
|
-
for (const unknown of unknownTokens) console.log(
|
|
909
|
-
const values =
|
|
2854
|
+
for (const err of errors) console.log(pc9.yellow("!"), err);
|
|
2855
|
+
for (const unknown of unknownTokens) console.log(pc9.yellow("!"), `"${unknown}" isn't a known token \u2014 skipped.`);
|
|
2856
|
+
const values = deriveForegrounds2(parsed);
|
|
910
2857
|
const setCount = Object.keys(values).length;
|
|
911
2858
|
if (setCount === 0) {
|
|
912
2859
|
throw new Error(`No tokens set in ${path9.relative(cwd, src)} \u2014 nothing to build. Uncomment at least one line.`);
|
|
913
2860
|
}
|
|
914
2861
|
const dest = cssPath(cwd, name);
|
|
915
|
-
await
|
|
916
|
-
console.log(
|
|
917
|
-
console.log(`Paste it after ${
|
|
2862
|
+
await writeFile4(dest, toCssBlock2(values), "utf8");
|
|
2863
|
+
console.log(pc9.green("\u2714"), `Wrote ${path9.relative(cwd, dest)} (${setCount} token${setCount === 1 ? "" : "s"})`);
|
|
2864
|
+
console.log(`Paste it after ${pc9.dim('@import "@kinetixui/tokens/css";')} in your global CSS.`);
|
|
918
2865
|
console.log();
|
|
919
|
-
console.log(
|
|
920
|
-
const results =
|
|
2866
|
+
console.log(pc9.dim("WCAG AA contrast (4.5:1)"));
|
|
2867
|
+
const results = checkContrast2(values);
|
|
921
2868
|
let failed = 0;
|
|
922
2869
|
for (const { pair, ratio, pass } of results) {
|
|
923
|
-
const mark = pass ?
|
|
2870
|
+
const mark = pass ? pc9.green("\u2714") : pc9.red("\u2716");
|
|
924
2871
|
if (!pass) failed++;
|
|
925
|
-
console.log(`${mark} ${pair[0]} / ${pair[1]}`,
|
|
2872
|
+
console.log(`${mark} ${pair[0]} / ${pair[1]}`, pc9.dim(`${ratio.toFixed(2)}:1`));
|
|
926
2873
|
}
|
|
927
2874
|
if (results.length === 0) {
|
|
928
|
-
console.log(
|
|
2875
|
+
console.log(pc9.dim("(no checkable pairs \u2014 set at least one base + foreground, or just a base to auto-derive one)"));
|
|
929
2876
|
}
|
|
930
2877
|
console.log();
|
|
931
2878
|
console.log("Native (SwiftUI/Compose/Flutter) theme output isn't built yet \u2014 this compiles to CSS only.");
|
|
932
|
-
console.log(
|
|
2879
|
+
console.log(pc9.dim("See https://kinetixui.com/docs/cli#theme for the current scope."));
|
|
933
2880
|
if (failed > 0 && options.failOnContrast) {
|
|
934
2881
|
process.exitCode = 1;
|
|
935
2882
|
}
|
|
@@ -938,7 +2885,7 @@ async function themeBuild(name, options) {
|
|
|
938
2885
|
// package.json
|
|
939
2886
|
var package_default = {
|
|
940
2887
|
name: "@kinetixui/cli",
|
|
941
|
-
version: "0.
|
|
2888
|
+
version: "0.23.0",
|
|
942
2889
|
description: "Add KinetixUI components and tokens to your project.",
|
|
943
2890
|
type: "module",
|
|
944
2891
|
license: "MIT",
|
|
@@ -961,6 +2908,8 @@ var package_default = {
|
|
|
961
2908
|
prompts: "^2.4.2"
|
|
962
2909
|
},
|
|
963
2910
|
devDependencies: {
|
|
2911
|
+
"@kinetixui/create-preset": "workspace:*",
|
|
2912
|
+
"@kinetixui/create-theme": "workspace:*",
|
|
964
2913
|
"@types/node": "^22.20.3",
|
|
965
2914
|
"@types/prompts": "^2.4.9",
|
|
966
2915
|
tsup: "^8.5.1",
|
|
@@ -982,7 +2931,7 @@ program.command("init").description("Set up kinetixui.json and pull in the token
|
|
|
982
2931
|
try {
|
|
983
2932
|
await init({ registry: opts.registry, yes: opts.yes });
|
|
984
2933
|
} catch (err) {
|
|
985
|
-
console.error(
|
|
2934
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
986
2935
|
process.exitCode = 1;
|
|
987
2936
|
}
|
|
988
2937
|
});
|
|
@@ -990,7 +2939,7 @@ program.command("add").description("Add one or more components to your project."
|
|
|
990
2939
|
try {
|
|
991
2940
|
await add(components, { registry: opts.registry, overwrite: opts.overwrite, all: opts.all });
|
|
992
2941
|
} catch (err) {
|
|
993
|
-
console.error(
|
|
2942
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
994
2943
|
process.exitCode = 1;
|
|
995
2944
|
}
|
|
996
2945
|
});
|
|
@@ -998,7 +2947,7 @@ program.command("list").description("List every component available in the regis
|
|
|
998
2947
|
try {
|
|
999
2948
|
await list({ registry: opts.registry });
|
|
1000
2949
|
} catch (err) {
|
|
1001
|
-
console.error(
|
|
2950
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1002
2951
|
process.exitCode = 1;
|
|
1003
2952
|
}
|
|
1004
2953
|
});
|
|
@@ -1006,7 +2955,7 @@ program.command("inspect").description("Show everything the registry knows about
|
|
|
1006
2955
|
try {
|
|
1007
2956
|
await inspect(name, { registry: opts.registry });
|
|
1008
2957
|
} catch (err) {
|
|
1009
|
-
console.error(
|
|
2958
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1010
2959
|
process.exitCode = 1;
|
|
1011
2960
|
}
|
|
1012
2961
|
});
|
|
@@ -1014,7 +2963,7 @@ program.command("parity").description("Show which platforms carry each component
|
|
|
1014
2963
|
try {
|
|
1015
2964
|
await parity(components, { registry: opts.registry });
|
|
1016
2965
|
} catch (err) {
|
|
1017
|
-
console.error(
|
|
2966
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1018
2967
|
process.exitCode = 1;
|
|
1019
2968
|
}
|
|
1020
2969
|
});
|
|
@@ -1022,7 +2971,7 @@ program.command("lint").description("Scan for hardcoded colors and spacing that
|
|
|
1022
2971
|
try {
|
|
1023
2972
|
await lint(target, { fail: opts.fail });
|
|
1024
2973
|
} catch (err) {
|
|
1025
|
-
console.error(
|
|
2974
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1026
2975
|
process.exitCode = 1;
|
|
1027
2976
|
}
|
|
1028
2977
|
});
|
|
@@ -1030,7 +2979,7 @@ program.command("doctor").description("Check kinetixui.json, its aliases, and th
|
|
|
1030
2979
|
try {
|
|
1031
2980
|
await doctor();
|
|
1032
2981
|
} catch (err) {
|
|
1033
|
-
console.error(
|
|
2982
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1034
2983
|
process.exitCode = 1;
|
|
1035
2984
|
}
|
|
1036
2985
|
});
|
|
@@ -1039,7 +2988,7 @@ theme.command("create").description("Scaffold kinetixui-themes/<name>.csv with e
|
|
|
1039
2988
|
try {
|
|
1040
2989
|
await themeCreate(name, { force: opts.force });
|
|
1041
2990
|
} catch (err) {
|
|
1042
|
-
console.error(
|
|
2991
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1043
2992
|
process.exitCode = 1;
|
|
1044
2993
|
}
|
|
1045
2994
|
});
|
|
@@ -1047,7 +2996,56 @@ theme.command("build").description("Compile kinetixui-themes/<name>.csv to CSS a
|
|
|
1047
2996
|
try {
|
|
1048
2997
|
await themeBuild(name, { failOnContrast: opts.failOnContrast });
|
|
1049
2998
|
} catch (err) {
|
|
1050
|
-
console.error(
|
|
2999
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3000
|
+
process.exitCode = 1;
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
var preset = program.command("preset").description("Read a Kinetix Create preset code (KX1_\u2026) produced by kinetixui.com/create.");
|
|
3004
|
+
preset.command("decode").description("Show what a preset contains. Accepts a KX1_ code or a full /create?preset=\u2026 URL.").argument("<preset>", "a KX1_ code, or a share URL").option("--json", "print the configuration as JSON", false).action((input, opts) => {
|
|
3005
|
+
try {
|
|
3006
|
+
presetDecode(input, { json: opts.json });
|
|
3007
|
+
} catch (err) {
|
|
3008
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3009
|
+
process.exitCode = 1;
|
|
3010
|
+
}
|
|
3011
|
+
});
|
|
3012
|
+
preset.command("css").description("Resolve a preset into its web CSS override block \u2014 the same output as Copy CSS in the workspace. Web CSS only; no native (SwiftUI/Compose/Flutter) output.").argument("<preset>", "a KX1_ code, or a share URL").option("-o, --output <file>", "write to a file instead of stdout").action(async (input, opts) => {
|
|
3013
|
+
try {
|
|
3014
|
+
await presetCss(input, { output: opts.output });
|
|
3015
|
+
} catch (err) {
|
|
3016
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3017
|
+
process.exitCode = 1;
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
preset.command("swiftui").description("Resolve a preset into a SwiftUI theme file (KinetixColors, light and dark). Colours only \u2014 SwiftUI has no radius or elevation token to carry. No Compose or Flutter output.").argument("<preset>", "a KX1_ code, or a share URL").option("-o, --output <file>", "write to a file instead of stdout").option("-n, --name <symbol>", "the Swift enum to generate", DEFAULT_SWIFT_SYMBOL).action(async (input, opts) => {
|
|
3021
|
+
try {
|
|
3022
|
+
await presetSwiftUi(input, { output: opts.output, name: opts.name });
|
|
3023
|
+
} catch (err) {
|
|
3024
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3025
|
+
process.exitCode = 1;
|
|
3026
|
+
}
|
|
3027
|
+
});
|
|
3028
|
+
preset.command("compose").description("Resolve a preset into a Jetpack Compose theme file (KinetixColors, light and dark). Colours only \u2014 Compose has no runtime radius or elevation token to carry. No Flutter or Android XML output.").argument("<preset>", "a KX1_ code, or a share URL").option("-o, --output <file>", "write to a file instead of stdout").option("-n, --name <symbol>", "the Kotlin object to generate", DEFAULT_COMPOSE_SYMBOL).action(async (input, opts) => {
|
|
3029
|
+
try {
|
|
3030
|
+
await presetCompose(input, { output: opts.output, name: opts.name });
|
|
3031
|
+
} catch (err) {
|
|
3032
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3033
|
+
process.exitCode = 1;
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
3036
|
+
preset.command("flutter").description("Resolve a preset into a Flutter theme file (KinetixColors, light and dark). Colours only \u2014 Flutter's radius and elevation are not runtime-themeable. No Android XML output.").argument("<preset>", "a KX1_ code, or a share URL").option("-o, --output <file>", "write to a file instead of stdout").option("-n, --name <symbol>", "the Dart class to generate", DEFAULT_FLUTTER_SYMBOL).action(async (input, opts) => {
|
|
3037
|
+
try {
|
|
3038
|
+
await presetFlutter(input, { output: opts.output, name: opts.name });
|
|
3039
|
+
} catch (err) {
|
|
3040
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
3041
|
+
process.exitCode = 1;
|
|
3042
|
+
}
|
|
3043
|
+
});
|
|
3044
|
+
preset.command("url").description("Print the canonical share URL for a preset. Prints it \u2014 piping it to your opener is up to you.").argument("<preset>", "a KX1_ code, or a share URL").option("-s, --site <origin>", "site origin", "https://kinetixui.com").action((input, opts) => {
|
|
3045
|
+
try {
|
|
3046
|
+
presetUrlCommand(input, { site: opts.site });
|
|
3047
|
+
} catch (err) {
|
|
3048
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1051
3049
|
process.exitCode = 1;
|
|
1052
3050
|
}
|
|
1053
3051
|
});
|