@kinetixui/cli 0.22.0 → 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 +2059 -55
- package/package.json +4 -2
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 "));
|
|
@@ -658,22 +658,283 @@ async function parity(names, options) {
|
|
|
658
658
|
}
|
|
659
659
|
console.log();
|
|
660
660
|
console.log(`${rows.length} of ${components.length} component(s) shown.`);
|
|
661
|
+
if (!names.length) {
|
|
662
|
+
console.log(
|
|
663
|
+
pc7.dim("Registry items only \u2014 components that ship inside another item, or are documented compositions, have no entry of their own.")
|
|
664
|
+
);
|
|
665
|
+
console.log(pc7.dim("Full coverage: https://kinetixui.com/docs/platforms"));
|
|
666
|
+
}
|
|
661
667
|
}
|
|
662
668
|
|
|
663
|
-
// src/commands/
|
|
664
|
-
import {
|
|
665
|
-
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
666
|
-
import path9 from "path";
|
|
669
|
+
// src/commands/preset.ts
|
|
670
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
667
671
|
import pc8 from "picocolors";
|
|
668
672
|
|
|
669
|
-
// 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
|
|
670
929
|
function hexToRgb(hex) {
|
|
671
930
|
const h = hex.replace("#", "");
|
|
672
931
|
const n = parseInt(h.length === 3 ? h.replace(/(.)/g, "$1$1") : h, 16);
|
|
673
932
|
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
674
933
|
}
|
|
675
934
|
function rgbToHsl(r, g, b) {
|
|
676
|
-
r /= 255
|
|
935
|
+
r /= 255;
|
|
936
|
+
g /= 255;
|
|
937
|
+
b /= 255;
|
|
677
938
|
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
678
939
|
let h = 0, s = 0;
|
|
679
940
|
const l = (max + min) / 2;
|
|
@@ -699,18 +960,46 @@ function hexToHslChannels(hex) {
|
|
|
699
960
|
const [h, s, l] = rgbToHsl(...hexToRgb(hex));
|
|
700
961
|
return `${h} ${s}% ${l}%`;
|
|
701
962
|
}
|
|
702
|
-
function
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
706
|
-
});
|
|
707
|
-
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);
|
|
708
966
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
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;
|
|
712
971
|
return la > lb ? la / lb : lb / la;
|
|
713
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
|
+
}
|
|
714
1003
|
function bestTextHex(bgHex) {
|
|
715
1004
|
return contrastRatio(bgHex, "#ffffff") >= contrastRatio(bgHex, "#000000") ? "#ffffff" : "#000000";
|
|
716
1005
|
}
|
|
@@ -722,8 +1011,1672 @@ function normalizeHex(v) {
|
|
|
722
1011
|
return `#${t.toLowerCase()}`;
|
|
723
1012
|
}
|
|
724
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
|
+
|
|
725
2678
|
// src/lib/theme.ts
|
|
726
|
-
var
|
|
2679
|
+
var ACCEPTED_TOKENS2 = [
|
|
727
2680
|
"background",
|
|
728
2681
|
"foreground",
|
|
729
2682
|
"card",
|
|
@@ -778,7 +2731,7 @@ var EXAMPLE_VALUES = {
|
|
|
778
2731
|
brand: "#0f172a",
|
|
779
2732
|
"brand-foreground": "#ffffff"
|
|
780
2733
|
};
|
|
781
|
-
var
|
|
2734
|
+
var FOREGROUND_OF2 = {
|
|
782
2735
|
background: "foreground",
|
|
783
2736
|
card: "card-foreground",
|
|
784
2737
|
popover: "popover-foreground",
|
|
@@ -790,7 +2743,7 @@ var FOREGROUND_OF = {
|
|
|
790
2743
|
action: "action-foreground",
|
|
791
2744
|
brand: "brand-foreground"
|
|
792
2745
|
};
|
|
793
|
-
var
|
|
2746
|
+
var CONTRAST_PAIRS2 = [
|
|
794
2747
|
["background", "foreground"],
|
|
795
2748
|
["card", "card-foreground"],
|
|
796
2749
|
["popover", "popover-foreground"],
|
|
@@ -802,7 +2755,7 @@ var CONTRAST_PAIRS = [
|
|
|
802
2755
|
["action", "action-foreground"],
|
|
803
2756
|
["brand", "brand-foreground"]
|
|
804
2757
|
];
|
|
805
|
-
var isAccepted = (t) =>
|
|
2758
|
+
var isAccepted = (t) => ACCEPTED_TOKENS2.includes(t);
|
|
806
2759
|
function parseThemeFile(text) {
|
|
807
2760
|
const values = {};
|
|
808
2761
|
const errors = [];
|
|
@@ -828,22 +2781,22 @@ function parseThemeFile(text) {
|
|
|
828
2781
|
});
|
|
829
2782
|
return { values, errors, unknownTokens };
|
|
830
2783
|
}
|
|
831
|
-
function
|
|
2784
|
+
function deriveForegrounds2(values) {
|
|
832
2785
|
const out = { ...values };
|
|
833
|
-
for (const [base, fg] of Object.entries(
|
|
2786
|
+
for (const [base, fg] of Object.entries(FOREGROUND_OF2)) {
|
|
834
2787
|
if (out[base] && !out[fg]) out[fg] = bestTextHex(out[base]);
|
|
835
2788
|
}
|
|
836
2789
|
return out;
|
|
837
2790
|
}
|
|
838
|
-
function
|
|
839
|
-
const lines =
|
|
2791
|
+
function toCssBlock2(values) {
|
|
2792
|
+
const lines = ACCEPTED_TOKENS2.filter((t) => values[t]).map((t) => ` --${t}: ${hexToHslChannels(values[t])};`);
|
|
840
2793
|
return `:root {
|
|
841
2794
|
${lines.join("\n")}
|
|
842
2795
|
}
|
|
843
2796
|
`;
|
|
844
2797
|
}
|
|
845
|
-
function
|
|
846
|
-
return
|
|
2798
|
+
function checkContrast2(values) {
|
|
2799
|
+
return CONTRAST_PAIRS2.filter(([bg, fg]) => values[bg] && values[fg]).map(([bg, fg]) => {
|
|
847
2800
|
const ratio = contrastRatio(values[bg], values[fg]);
|
|
848
2801
|
return { pair: [bg, fg], ratio, pass: ratio >= 4.5 };
|
|
849
2802
|
});
|
|
@@ -862,7 +2815,7 @@ function scaffoldThemeFile(name) {
|
|
|
862
2815
|
"# don't have to set both unless you want a specific foreground.",
|
|
863
2816
|
"#"
|
|
864
2817
|
];
|
|
865
|
-
for (const token of
|
|
2818
|
+
for (const token of ACCEPTED_TOKENS2) {
|
|
866
2819
|
lines.push(`# ${token},${EXAMPLE_VALUES[token]}`);
|
|
867
2820
|
}
|
|
868
2821
|
return lines.join("\n") + "\n";
|
|
@@ -884,10 +2837,10 @@ async function themeCreate(name, options) {
|
|
|
884
2837
|
throw new Error(`${path9.relative(cwd, file)} already exists. Pass --force to overwrite it.`);
|
|
885
2838
|
}
|
|
886
2839
|
await mkdir2(path9.dirname(file), { recursive: true });
|
|
887
|
-
await
|
|
888
|
-
console.log(
|
|
2840
|
+
await writeFile4(file, scaffoldThemeFile(name), "utf8");
|
|
2841
|
+
console.log(pc9.green("\u2714"), `Wrote ${path9.relative(cwd, file)}`);
|
|
889
2842
|
console.log(`Uncomment and edit the tokens you want to override, then run:`);
|
|
890
|
-
console.log(
|
|
2843
|
+
console.log(pc9.dim(` kinetixui theme build ${name}`));
|
|
891
2844
|
}
|
|
892
2845
|
async function themeBuild(name, options) {
|
|
893
2846
|
assertThemeName(name);
|
|
@@ -898,32 +2851,32 @@ async function themeBuild(name, options) {
|
|
|
898
2851
|
}
|
|
899
2852
|
const text = await readFile3(src, "utf8");
|
|
900
2853
|
const { values: parsed, errors, unknownTokens } = parseThemeFile(text);
|
|
901
|
-
for (const err of errors) console.log(
|
|
902
|
-
for (const unknown of unknownTokens) console.log(
|
|
903
|
-
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);
|
|
904
2857
|
const setCount = Object.keys(values).length;
|
|
905
2858
|
if (setCount === 0) {
|
|
906
2859
|
throw new Error(`No tokens set in ${path9.relative(cwd, src)} \u2014 nothing to build. Uncomment at least one line.`);
|
|
907
2860
|
}
|
|
908
2861
|
const dest = cssPath(cwd, name);
|
|
909
|
-
await
|
|
910
|
-
console.log(
|
|
911
|
-
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.`);
|
|
912
2865
|
console.log();
|
|
913
|
-
console.log(
|
|
914
|
-
const results =
|
|
2866
|
+
console.log(pc9.dim("WCAG AA contrast (4.5:1)"));
|
|
2867
|
+
const results = checkContrast2(values);
|
|
915
2868
|
let failed = 0;
|
|
916
2869
|
for (const { pair, ratio, pass } of results) {
|
|
917
|
-
const mark = pass ?
|
|
2870
|
+
const mark = pass ? pc9.green("\u2714") : pc9.red("\u2716");
|
|
918
2871
|
if (!pass) failed++;
|
|
919
|
-
console.log(`${mark} ${pair[0]} / ${pair[1]}`,
|
|
2872
|
+
console.log(`${mark} ${pair[0]} / ${pair[1]}`, pc9.dim(`${ratio.toFixed(2)}:1`));
|
|
920
2873
|
}
|
|
921
2874
|
if (results.length === 0) {
|
|
922
|
-
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)"));
|
|
923
2876
|
}
|
|
924
2877
|
console.log();
|
|
925
2878
|
console.log("Native (SwiftUI/Compose/Flutter) theme output isn't built yet \u2014 this compiles to CSS only.");
|
|
926
|
-
console.log(
|
|
2879
|
+
console.log(pc9.dim("See https://kinetixui.com/docs/cli#theme for the current scope."));
|
|
927
2880
|
if (failed > 0 && options.failOnContrast) {
|
|
928
2881
|
process.exitCode = 1;
|
|
929
2882
|
}
|
|
@@ -932,7 +2885,7 @@ async function themeBuild(name, options) {
|
|
|
932
2885
|
// package.json
|
|
933
2886
|
var package_default = {
|
|
934
2887
|
name: "@kinetixui/cli",
|
|
935
|
-
version: "0.
|
|
2888
|
+
version: "0.23.0",
|
|
936
2889
|
description: "Add KinetixUI components and tokens to your project.",
|
|
937
2890
|
type: "module",
|
|
938
2891
|
license: "MIT",
|
|
@@ -955,7 +2908,9 @@ var package_default = {
|
|
|
955
2908
|
prompts: "^2.4.2"
|
|
956
2909
|
},
|
|
957
2910
|
devDependencies: {
|
|
958
|
-
"@
|
|
2911
|
+
"@kinetixui/create-preset": "workspace:*",
|
|
2912
|
+
"@kinetixui/create-theme": "workspace:*",
|
|
2913
|
+
"@types/node": "^22.20.3",
|
|
959
2914
|
"@types/prompts": "^2.4.9",
|
|
960
2915
|
tsup: "^8.5.1",
|
|
961
2916
|
typescript: "^5.9.3"
|
|
@@ -976,7 +2931,7 @@ program.command("init").description("Set up kinetixui.json and pull in the token
|
|
|
976
2931
|
try {
|
|
977
2932
|
await init({ registry: opts.registry, yes: opts.yes });
|
|
978
2933
|
} catch (err) {
|
|
979
|
-
console.error(
|
|
2934
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
980
2935
|
process.exitCode = 1;
|
|
981
2936
|
}
|
|
982
2937
|
});
|
|
@@ -984,7 +2939,7 @@ program.command("add").description("Add one or more components to your project."
|
|
|
984
2939
|
try {
|
|
985
2940
|
await add(components, { registry: opts.registry, overwrite: opts.overwrite, all: opts.all });
|
|
986
2941
|
} catch (err) {
|
|
987
|
-
console.error(
|
|
2942
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
988
2943
|
process.exitCode = 1;
|
|
989
2944
|
}
|
|
990
2945
|
});
|
|
@@ -992,7 +2947,7 @@ program.command("list").description("List every component available in the regis
|
|
|
992
2947
|
try {
|
|
993
2948
|
await list({ registry: opts.registry });
|
|
994
2949
|
} catch (err) {
|
|
995
|
-
console.error(
|
|
2950
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
996
2951
|
process.exitCode = 1;
|
|
997
2952
|
}
|
|
998
2953
|
});
|
|
@@ -1000,7 +2955,7 @@ program.command("inspect").description("Show everything the registry knows about
|
|
|
1000
2955
|
try {
|
|
1001
2956
|
await inspect(name, { registry: opts.registry });
|
|
1002
2957
|
} catch (err) {
|
|
1003
|
-
console.error(
|
|
2958
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1004
2959
|
process.exitCode = 1;
|
|
1005
2960
|
}
|
|
1006
2961
|
});
|
|
@@ -1008,7 +2963,7 @@ program.command("parity").description("Show which platforms carry each component
|
|
|
1008
2963
|
try {
|
|
1009
2964
|
await parity(components, { registry: opts.registry });
|
|
1010
2965
|
} catch (err) {
|
|
1011
|
-
console.error(
|
|
2966
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1012
2967
|
process.exitCode = 1;
|
|
1013
2968
|
}
|
|
1014
2969
|
});
|
|
@@ -1016,7 +2971,7 @@ program.command("lint").description("Scan for hardcoded colors and spacing that
|
|
|
1016
2971
|
try {
|
|
1017
2972
|
await lint(target, { fail: opts.fail });
|
|
1018
2973
|
} catch (err) {
|
|
1019
|
-
console.error(
|
|
2974
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1020
2975
|
process.exitCode = 1;
|
|
1021
2976
|
}
|
|
1022
2977
|
});
|
|
@@ -1024,7 +2979,7 @@ program.command("doctor").description("Check kinetixui.json, its aliases, and th
|
|
|
1024
2979
|
try {
|
|
1025
2980
|
await doctor();
|
|
1026
2981
|
} catch (err) {
|
|
1027
|
-
console.error(
|
|
2982
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1028
2983
|
process.exitCode = 1;
|
|
1029
2984
|
}
|
|
1030
2985
|
});
|
|
@@ -1033,7 +2988,7 @@ theme.command("create").description("Scaffold kinetixui-themes/<name>.csv with e
|
|
|
1033
2988
|
try {
|
|
1034
2989
|
await themeCreate(name, { force: opts.force });
|
|
1035
2990
|
} catch (err) {
|
|
1036
|
-
console.error(
|
|
2991
|
+
console.error(pc10.red("\u2716"), err.message);
|
|
1037
2992
|
process.exitCode = 1;
|
|
1038
2993
|
}
|
|
1039
2994
|
});
|
|
@@ -1041,7 +2996,56 @@ theme.command("build").description("Compile kinetixui-themes/<name>.csv to CSS a
|
|
|
1041
2996
|
try {
|
|
1042
2997
|
await themeBuild(name, { failOnContrast: opts.failOnContrast });
|
|
1043
2998
|
} catch (err) {
|
|
1044
|
-
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);
|
|
1045
3049
|
process.exitCode = 1;
|
|
1046
3050
|
}
|
|
1047
3051
|
});
|