@open-feishu-card/adapter 0.3.2 → 0.3.4
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/custom-colors.d.ts +26 -0
- package/dist/custom-colors.d.ts.map +1 -0
- package/dist/index.cjs +324 -125
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +316 -126
- package/dist/normalize-stored-colors.d.ts +12 -1
- package/dist/normalize-stored-colors.d.ts.map +1 -1
- package/dist/raw-to-dsl.d.ts +2 -1
- package/dist/raw-to-dsl.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feishu RAW editor fields for theme-aware custom colors:
|
|
3
|
+
* customBackgroundStyle / custom_background_style → column backgrounds
|
|
4
|
+
* textStyle.customColor / textStyle.custom_color → inline text color
|
|
5
|
+
*
|
|
6
|
+
* Public DSL stores custom colors in config.style.color (cus-accent / cus-bg-N …)
|
|
7
|
+
* and references them via markdown.font_color / background_style — not inline #hex.
|
|
8
|
+
*/
|
|
9
|
+
import type { CustomColorEntry } from './normalize-stored-colors.js';
|
|
10
|
+
export type ThemeColorPair = {
|
|
11
|
+
light_mode?: string;
|
|
12
|
+
lightMode?: string;
|
|
13
|
+
dark_mode?: string;
|
|
14
|
+
darkMode?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function readThemeColorLight(pair: unknown): string | undefined;
|
|
17
|
+
export declare function readThemeColorDark(pair: unknown): string | undefined;
|
|
18
|
+
export declare function isCustomHexColor(color: string): boolean;
|
|
19
|
+
/** Enum `color` wins; else fall back to customColor light hex/rgba. */
|
|
20
|
+
export declare function resolveTextStyleColor(textStyle: unknown): string | undefined;
|
|
21
|
+
/** When text color comes from customColor (not enum), return the full light/dark pair. */
|
|
22
|
+
export declare function resolveTextStyleCustomColorPair(textStyle: unknown): ThemeColorPair | undefined;
|
|
23
|
+
export declare function themePairToColorEntry(pair: ThemeColorPair): CustomColorEntry | undefined;
|
|
24
|
+
/** Light hex/rgba from column (or container) custom background, if any. */
|
|
25
|
+
export declare function resolveCustomBackgroundLight(property: unknown): string | undefined;
|
|
26
|
+
//# sourceMappingURL=custom-colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-colors.d.ts","sourceRoot":"","sources":["../src/custom-colors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOrE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOpE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAO5E;AAED,0FAA0F;AAC1F,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAQ9F;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAQxF;AAED,2EAA2E;AAC3E,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKlF"}
|
package/dist/index.cjs
CHANGED
|
@@ -868,6 +868,256 @@ function isRecord(v) {
|
|
|
868
868
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
869
869
|
}
|
|
870
870
|
|
|
871
|
+
// src/normalize-stored-colors.ts
|
|
872
|
+
function createColorRegistry() {
|
|
873
|
+
return { entries: {}, keyToAlias: /* @__PURE__ */ new Map(), bgSeq: 0, textSeq: 0 };
|
|
874
|
+
}
|
|
875
|
+
function colorEntryKey(entry) {
|
|
876
|
+
return `${entry.light_mode}|${entry.dark_mode}`;
|
|
877
|
+
}
|
|
878
|
+
function registerColorEntry(registry, entry, prefix) {
|
|
879
|
+
const key = colorEntryKey(entry);
|
|
880
|
+
const existing = registry.keyToAlias.get(key);
|
|
881
|
+
if (existing) return existing;
|
|
882
|
+
const alias = prefix === "cus-bg" ? `cus-bg-${registry.bgSeq++}` : `cus-text-${registry.textSeq++}`;
|
|
883
|
+
registry.entries[alias] = entry;
|
|
884
|
+
registry.keyToAlias.set(key, alias);
|
|
885
|
+
return alias;
|
|
886
|
+
}
|
|
887
|
+
function hexColorToRgba(hex) {
|
|
888
|
+
const h = hex.trim().replace(/^#/, "");
|
|
889
|
+
if (h.length === 8) {
|
|
890
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
891
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
892
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
893
|
+
const a = Math.round(parseInt(h.slice(6, 8), 16) / 255 * 100) / 100;
|
|
894
|
+
return `rgba(${r},${g},${b},${a})`;
|
|
895
|
+
}
|
|
896
|
+
if (h.length === 6) {
|
|
897
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
898
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
899
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
900
|
+
return `rgba(${r},${g},${b},1)`;
|
|
901
|
+
}
|
|
902
|
+
return void 0;
|
|
903
|
+
}
|
|
904
|
+
function readCustomBackgroundStyle(node) {
|
|
905
|
+
const raw = node.custom_background_style;
|
|
906
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
907
|
+
const o = raw;
|
|
908
|
+
const lightRaw = typeof o.light_mode === "string" ? o.light_mode : void 0;
|
|
909
|
+
const darkRaw = typeof o.dark_mode === "string" ? o.dark_mode : void 0;
|
|
910
|
+
if (!lightRaw || !darkRaw) return void 0;
|
|
911
|
+
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
912
|
+
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
913
|
+
if (!light || !dark) return void 0;
|
|
914
|
+
return { light_mode: light, dark_mode: dark };
|
|
915
|
+
}
|
|
916
|
+
function normalizeStoredCardColors(dsl, preRegistered) {
|
|
917
|
+
const colorEntries = { ...preRegistered?.entries ?? {} };
|
|
918
|
+
const keyToAlias = new Map(preRegistered?.keyToAlias ?? []);
|
|
919
|
+
let bgSeq = preRegistered?.bgSeq ?? 0;
|
|
920
|
+
let textSeq = preRegistered?.textSeq ?? 0;
|
|
921
|
+
const ensureAlias = (prefix, entry) => {
|
|
922
|
+
const existing2 = keyToAlias.get(colorEntryKey(entry));
|
|
923
|
+
if (existing2) return existing2;
|
|
924
|
+
const alias = prefix === "cus-bg" ? `cus-bg-${bgSeq++}` : `cus-text-${textSeq++}`;
|
|
925
|
+
colorEntries[alias] = entry;
|
|
926
|
+
keyToAlias.set(colorEntryKey(entry), alias);
|
|
927
|
+
return alias;
|
|
928
|
+
};
|
|
929
|
+
const walk = (node) => {
|
|
930
|
+
if (Array.isArray(node)) {
|
|
931
|
+
for (const child of node) walk(child);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (!node || typeof node !== "object") return;
|
|
935
|
+
const obj = node;
|
|
936
|
+
if (obj.tag === "column") {
|
|
937
|
+
const custom = readCustomBackgroundStyle(obj);
|
|
938
|
+
if (custom) {
|
|
939
|
+
obj.background_style = ensureAlias("cus-bg", custom);
|
|
940
|
+
delete obj.custom_background_style;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
if (obj.tag === "markdown" && typeof obj.content === "string") {
|
|
944
|
+
normalizeMarkdownInlineHexColors(obj, ensureAlias);
|
|
945
|
+
}
|
|
946
|
+
for (const value of Object.values(obj)) walk(value);
|
|
947
|
+
};
|
|
948
|
+
walk(dsl.body);
|
|
949
|
+
if (Object.keys(colorEntries).length === 0) return;
|
|
950
|
+
const firstBgKey = Object.keys(colorEntries).find((k) => k.startsWith("cus-bg-"));
|
|
951
|
+
const firstBg = firstBgKey ? colorEntries[firstBgKey] : void 0;
|
|
952
|
+
if (firstBg) {
|
|
953
|
+
const m = firstBg.light_mode.match(/^rgba\((\d+),(\d+),(\d+),/);
|
|
954
|
+
if (m) {
|
|
955
|
+
colorEntries["cus-accent"] = {
|
|
956
|
+
light_mode: `rgba(${m[1]},${m[2]},${m[3]},1)`,
|
|
957
|
+
dark_mode: firstBg.dark_mode.replace(/,\s*[\d.]+\)$/, ",1)")
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
colorEntries["cus-muted"] ??= {
|
|
962
|
+
light_mode: "rgba(100,106,115,1)",
|
|
963
|
+
dark_mode: "rgba(166,166,166,1)"
|
|
964
|
+
};
|
|
965
|
+
const aliasRenames = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const to of ["cus-accent", "cus-muted"]) {
|
|
967
|
+
for (const [from, target] of unifyMatchingAlias(colorEntries, "cus-text", to)) {
|
|
968
|
+
aliasRenames.set(from, target);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
remapColorReferences(dsl.body, aliasRenames);
|
|
972
|
+
const config = dsl.config && typeof dsl.config === "object" ? dsl.config : {};
|
|
973
|
+
dsl.config = config;
|
|
974
|
+
const style = config.style && typeof config.style === "object" ? config.style : {};
|
|
975
|
+
config.style = style;
|
|
976
|
+
const existing = style.color && typeof style.color === "object" ? style.color : {};
|
|
977
|
+
style.color = { ...colorEntries, ...existing };
|
|
978
|
+
if (!colorEntries["cus-accent"]) return;
|
|
979
|
+
const applyDefaultFontColors = (node) => {
|
|
980
|
+
if (Array.isArray(node)) {
|
|
981
|
+
for (const child of node) applyDefaultFontColors(child);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
if (!node || typeof node !== "object") return;
|
|
985
|
+
const obj = node;
|
|
986
|
+
if (obj.tag === "markdown" && obj.font_color === void 0) {
|
|
987
|
+
const size = obj.text_size;
|
|
988
|
+
const content = typeof obj.content === "string" ? obj.content : "";
|
|
989
|
+
if (size === "small") obj.font_color = "cus-muted";
|
|
990
|
+
else if (size === "heading" || content.includes("**")) obj.font_color = "cus-accent";
|
|
991
|
+
}
|
|
992
|
+
for (const value of Object.values(obj)) applyDefaultFontColors(value);
|
|
993
|
+
};
|
|
994
|
+
applyDefaultFontColors(dsl.body);
|
|
995
|
+
}
|
|
996
|
+
function unifyMatchingAlias(entries, fromPrefix, to) {
|
|
997
|
+
const renames = /* @__PURE__ */ new Map();
|
|
998
|
+
const target = entries[to];
|
|
999
|
+
if (!target) return renames;
|
|
1000
|
+
for (const alias of Object.keys(entries)) {
|
|
1001
|
+
if (alias === to) continue;
|
|
1002
|
+
if (!alias.startsWith(`${fromPrefix}-`) && alias !== fromPrefix) continue;
|
|
1003
|
+
const entry = entries[alias];
|
|
1004
|
+
const matches = to === "cus-accent" || to === "cus-muted" ? entry.light_mode === target.light_mode : colorEntryKey(entry) === colorEntryKey(target);
|
|
1005
|
+
if (matches) {
|
|
1006
|
+
renames.set(alias, to);
|
|
1007
|
+
delete entries[alias];
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return renames;
|
|
1011
|
+
}
|
|
1012
|
+
function remapColorReferences(node, aliasRenames) {
|
|
1013
|
+
const remap = (alias) => {
|
|
1014
|
+
if (!alias) return alias;
|
|
1015
|
+
return aliasRenames.get(alias) ?? alias;
|
|
1016
|
+
};
|
|
1017
|
+
const walk = (n) => {
|
|
1018
|
+
if (Array.isArray(n)) {
|
|
1019
|
+
for (const c of n) walk(c);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (!n || typeof n !== "object") return;
|
|
1023
|
+
const obj = n;
|
|
1024
|
+
if (typeof obj.font_color === "string") obj.font_color = remap(obj.font_color);
|
|
1025
|
+
if (typeof obj.content === "string" && obj.content.includes("<font")) {
|
|
1026
|
+
obj.content = obj.content.replace(
|
|
1027
|
+
/<font color=['"]([^'"]+)['"]>/g,
|
|
1028
|
+
(_m, color) => {
|
|
1029
|
+
const next = remap(color);
|
|
1030
|
+
return next ? `<font color='${next}'>` : _m;
|
|
1031
|
+
}
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
for (const value of Object.values(obj)) walk(value);
|
|
1035
|
+
};
|
|
1036
|
+
walk(node);
|
|
1037
|
+
}
|
|
1038
|
+
function normalizeMarkdownInlineHexColors(obj, ensureAlias) {
|
|
1039
|
+
const content = obj.content;
|
|
1040
|
+
const hexColor = (raw) => {
|
|
1041
|
+
const v = raw.trim();
|
|
1042
|
+
if (v.startsWith("#")) {
|
|
1043
|
+
const rgba = hexColorToRgba(v);
|
|
1044
|
+
return rgba ? { light_mode: rgba, dark_mode: rgba } : void 0;
|
|
1045
|
+
}
|
|
1046
|
+
if (/^rgba?\(/i.test(v)) return { light_mode: v, dark_mode: v };
|
|
1047
|
+
return void 0;
|
|
1048
|
+
};
|
|
1049
|
+
const single = content.match(/^<font color=['"]([^'"]+)['"]\s*>([\s\S]*)<\/font>$/);
|
|
1050
|
+
if (single) {
|
|
1051
|
+
const entry = hexColor(single[1]);
|
|
1052
|
+
if (entry) {
|
|
1053
|
+
const alias = ensureAlias("cus-text", entry);
|
|
1054
|
+
if (obj.font_color === void 0) obj.font_color = alias;
|
|
1055
|
+
obj.content = single[2];
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
obj.content = content.replace(/<font color=['"]([^'"]+)['"]>/g, (match, raw) => {
|
|
1060
|
+
const entry = hexColor(raw);
|
|
1061
|
+
if (!entry) return match;
|
|
1062
|
+
const alias = ensureAlias("cus-text", entry);
|
|
1063
|
+
return `<font color='${alias}'>`;
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/custom-colors.ts
|
|
1068
|
+
function readThemeColorLight(pair) {
|
|
1069
|
+
if (!pair || typeof pair !== "object") return void 0;
|
|
1070
|
+
const o = pair;
|
|
1071
|
+
const raw = o.light_mode ?? o.lightMode;
|
|
1072
|
+
if (typeof raw !== "string") return void 0;
|
|
1073
|
+
const v = raw.trim();
|
|
1074
|
+
return v || void 0;
|
|
1075
|
+
}
|
|
1076
|
+
function readThemeColorDark(pair) {
|
|
1077
|
+
if (!pair || typeof pair !== "object") return void 0;
|
|
1078
|
+
const o = pair;
|
|
1079
|
+
const raw = o.dark_mode ?? o.darkMode;
|
|
1080
|
+
if (typeof raw !== "string") return void 0;
|
|
1081
|
+
const v = raw.trim();
|
|
1082
|
+
return v || void 0;
|
|
1083
|
+
}
|
|
1084
|
+
function isCustomHexColor(color) {
|
|
1085
|
+
const v = color.trim();
|
|
1086
|
+
return v.startsWith("#") || /^rgba?\(/i.test(v);
|
|
1087
|
+
}
|
|
1088
|
+
function resolveTextStyleColor(textStyle) {
|
|
1089
|
+
if (!textStyle || typeof textStyle !== "object") return void 0;
|
|
1090
|
+
const ts = textStyle;
|
|
1091
|
+
const enumColor = typeof ts.color === "string" ? ts.color.trim() : void 0;
|
|
1092
|
+
if (enumColor && enumColor !== "default") return enumColor;
|
|
1093
|
+
const custom = ts.custom_color ?? ts.customColor;
|
|
1094
|
+
return readThemeColorLight(custom);
|
|
1095
|
+
}
|
|
1096
|
+
function resolveTextStyleCustomColorPair(textStyle) {
|
|
1097
|
+
if (!textStyle || typeof textStyle !== "object") return void 0;
|
|
1098
|
+
const ts = textStyle;
|
|
1099
|
+
const enumColor = typeof ts.color === "string" ? ts.color.trim() : void 0;
|
|
1100
|
+
if (enumColor && enumColor !== "default") return void 0;
|
|
1101
|
+
const custom = ts.custom_color ?? ts.customColor;
|
|
1102
|
+
if (!custom || typeof custom !== "object") return void 0;
|
|
1103
|
+
return custom;
|
|
1104
|
+
}
|
|
1105
|
+
function themePairToColorEntry(pair) {
|
|
1106
|
+
const lightRaw = readThemeColorLight(pair);
|
|
1107
|
+
const darkRaw = readThemeColorDark(pair);
|
|
1108
|
+
if (!lightRaw || !darkRaw) return void 0;
|
|
1109
|
+
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
1110
|
+
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
1111
|
+
if (!light || !dark) return void 0;
|
|
1112
|
+
return { light_mode: light, dark_mode: dark };
|
|
1113
|
+
}
|
|
1114
|
+
function resolveCustomBackgroundLight(property) {
|
|
1115
|
+
if (!property || typeof property !== "object") return void 0;
|
|
1116
|
+
const p = property;
|
|
1117
|
+
const custom = p.custom_background_style ?? p.customBackgroundStyle;
|
|
1118
|
+
return readThemeColorLight(custom);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
871
1121
|
// src/raw-to-dsl.ts
|
|
872
1122
|
function isRawCardEnvelope(value) {
|
|
873
1123
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -883,12 +1133,19 @@ function rawCardToDsl(envelope) {
|
|
|
883
1133
|
} catch {
|
|
884
1134
|
return envelope;
|
|
885
1135
|
}
|
|
886
|
-
const ctx = {
|
|
1136
|
+
const ctx = makeCtx({
|
|
887
1137
|
images: env.json_attachment?.images ?? {},
|
|
888
1138
|
atUsers: env.json_attachment?.at_users ?? {}
|
|
889
|
-
};
|
|
1139
|
+
});
|
|
890
1140
|
return convertRoot(inner, ctx);
|
|
891
1141
|
}
|
|
1142
|
+
function makeCtx(partial) {
|
|
1143
|
+
return {
|
|
1144
|
+
images: partial?.images ?? {},
|
|
1145
|
+
atUsers: partial?.atUsers ?? {},
|
|
1146
|
+
colors: createColorRegistry()
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
892
1149
|
var RAW_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
893
1150
|
"new_body",
|
|
894
1151
|
"source",
|
|
@@ -976,6 +1233,7 @@ function convertRoot(root, ctx) {
|
|
|
976
1233
|
out.config = convertConfig(isRecord2(root.config) ? root.config : {});
|
|
977
1234
|
if (isRecord2(root.body)) out.body = convertBody2(root.body, ctx);
|
|
978
1235
|
if (isRecord2(root.header)) out.header = convertHeader2(root.header, ctx);
|
|
1236
|
+
normalizeStoredCardColors(out, ctx.colors);
|
|
979
1237
|
return out;
|
|
980
1238
|
}
|
|
981
1239
|
function convertConfig(_node) {
|
|
@@ -1077,7 +1335,7 @@ function unwrap(node, ctx) {
|
|
|
1077
1335
|
let value = isRecord2(cell) && "data" in cell ? cell.data : cell;
|
|
1078
1336
|
if (isRecord2(value) && value.tag === "markdown" && isRecord2(value.property)) {
|
|
1079
1337
|
const elements = Array.isArray(value.property.elements) ? value.property.elements : [];
|
|
1080
|
-
value = renderChildren(elements, ctx);
|
|
1338
|
+
value = renderChildren(elements, ctx).content;
|
|
1081
1339
|
}
|
|
1082
1340
|
r[k] = value;
|
|
1083
1341
|
}
|
|
@@ -1452,8 +1710,9 @@ function resolveAtUserId(prop, ctx) {
|
|
|
1452
1710
|
function rebuildMarkdown(property, ctx) {
|
|
1453
1711
|
const out = { tag: "markdown" };
|
|
1454
1712
|
const elements = Array.isArray(property.elements) ? property.elements : [];
|
|
1455
|
-
const content = renderChildren(elements, ctx);
|
|
1713
|
+
const { content, uniformFontColor } = renderChildren(elements, ctx);
|
|
1456
1714
|
if (content) out.content = content;
|
|
1715
|
+
if (uniformFontColor) out.font_color = uniformFontColor;
|
|
1457
1716
|
for (const [rawKey, rawValue] of Object.entries(property)) {
|
|
1458
1717
|
if (rawKey === "elements" || rawKey === "markdownElements" || rawKey === "originTag") continue;
|
|
1459
1718
|
if (rawKey === "udIcon") continue;
|
|
@@ -1471,7 +1730,7 @@ function rebuildMarkdown(property, ctx) {
|
|
|
1471
1730
|
}
|
|
1472
1731
|
return out;
|
|
1473
1732
|
}
|
|
1474
|
-
function wrapEmphasis(text, bold, color) {
|
|
1733
|
+
function wrapEmphasis(text, bold, color, colorKind, inlineCustomFont = false) {
|
|
1475
1734
|
if (!bold && !color) return text;
|
|
1476
1735
|
const lead = text.match(/^\s*/)[0];
|
|
1477
1736
|
const trail = text.match(/\s*$/)[0];
|
|
@@ -1479,20 +1738,51 @@ function wrapEmphasis(text, bold, color) {
|
|
|
1479
1738
|
if (core.length === 0) return text;
|
|
1480
1739
|
let wrapped = core;
|
|
1481
1740
|
if (bold) wrapped = `**${wrapped}**`;
|
|
1482
|
-
if (color)
|
|
1741
|
+
if (color) {
|
|
1742
|
+
if (colorKind === "enum") wrapped = `<font color='${color}'>${wrapped}</font>`;
|
|
1743
|
+
else if (colorKind === "custom" && inlineCustomFont) wrapped = `<font color='${color}'>${wrapped}</font>`;
|
|
1744
|
+
}
|
|
1483
1745
|
return lead + wrapped + trail;
|
|
1484
1746
|
}
|
|
1747
|
+
function resolveInlineStyle(ts, ctx) {
|
|
1748
|
+
const bold = !!(ts && Array.isArray(ts.attributes) && ts.attributes.includes("bold"));
|
|
1749
|
+
const customPair = resolveTextStyleCustomColorPair(ts);
|
|
1750
|
+
if (customPair) {
|
|
1751
|
+
const entry = themePairToColorEntry(customPair);
|
|
1752
|
+
if (entry) {
|
|
1753
|
+
const alias = registerColorEntry(ctx.colors, entry, "cus-text");
|
|
1754
|
+
return { bold, color: alias, colorKind: "custom" };
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
const rawColor = resolveTextStyleColor(ts);
|
|
1758
|
+
const color = rawColor === "default" ? void 0 : rawColor;
|
|
1759
|
+
if (!color) return { bold };
|
|
1760
|
+
if (isCustomHexColor(color)) {
|
|
1761
|
+
const rgba = color.startsWith("#") ? themePairToColorEntry({ lightMode: color, darkMode: color }) : { light_mode: color, dark_mode: color };
|
|
1762
|
+
if (rgba) {
|
|
1763
|
+
const alias = registerColorEntry(ctx.colors, rgba, "cus-text");
|
|
1764
|
+
return { bold, color: alias, colorKind: "custom" };
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return { bold, color, colorKind: "enum" };
|
|
1768
|
+
}
|
|
1485
1769
|
function renderChildren(children, ctx) {
|
|
1486
1770
|
let out = "";
|
|
1487
1771
|
let buffer = [];
|
|
1772
|
+
let uniformFontColor;
|
|
1488
1773
|
function flushBuffer() {
|
|
1489
1774
|
if (buffer.length === 0) return;
|
|
1775
|
+
const customColors = buffer.filter((s) => s.colorKind === "custom" && s.color).map((s) => s.color);
|
|
1776
|
+
const hasEnumColor = buffer.some((s) => s.colorKind === "enum" && s.color);
|
|
1777
|
+
const uniformCustom = customColors.length > 0 && !hasEnumColor && customColors.every((c) => c === customColors[0]) ? customColors[0] : void 0;
|
|
1778
|
+
if (uniformCustom) uniformFontColor = uniformCustom;
|
|
1490
1779
|
let group = [];
|
|
1491
1780
|
function emitGroup() {
|
|
1492
1781
|
if (group.length === 0) return;
|
|
1493
1782
|
const inner = group.map((s) => s.content).join("");
|
|
1494
|
-
const { bold, color } = group[0];
|
|
1495
|
-
|
|
1783
|
+
const { bold, color, colorKind } = group[0];
|
|
1784
|
+
const inlineCustomFont = colorKind === "custom" && !!color && color !== uniformCustom;
|
|
1785
|
+
out += wrapEmphasis(inner, bold, color, colorKind, inlineCustomFont);
|
|
1496
1786
|
group = [];
|
|
1497
1787
|
}
|
|
1498
1788
|
for (const seg of buffer) {
|
|
@@ -1501,8 +1791,9 @@ function renderChildren(children, ctx) {
|
|
|
1501
1791
|
continue;
|
|
1502
1792
|
}
|
|
1503
1793
|
const head = group[0];
|
|
1504
|
-
if (head.bold === seg.bold && head.color === seg.color
|
|
1505
|
-
|
|
1794
|
+
if (head.bold === seg.bold && head.color === seg.color && head.colorKind === seg.colorKind) {
|
|
1795
|
+
group.push(seg);
|
|
1796
|
+
} else {
|
|
1506
1797
|
emitGroup();
|
|
1507
1798
|
group.push(seg);
|
|
1508
1799
|
}
|
|
@@ -1521,33 +1812,33 @@ function renderChildren(children, ctx) {
|
|
|
1521
1812
|
out += childToMarkdown(child, ctx);
|
|
1522
1813
|
}
|
|
1523
1814
|
flushBuffer();
|
|
1524
|
-
return out;
|
|
1815
|
+
return { content: out, uniformFontColor };
|
|
1525
1816
|
}
|
|
1526
1817
|
function childToInlineSegment(child, ctx) {
|
|
1527
1818
|
const tag = child.tag;
|
|
1528
1819
|
const prop = isRecord2(child.property) ? child.property : {};
|
|
1529
1820
|
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1530
|
-
const bold
|
|
1531
|
-
const rawColor = ts && typeof ts.color === "string" ? ts.color : void 0;
|
|
1532
|
-
const color = rawColor === "default" ? void 0 : rawColor;
|
|
1821
|
+
const { bold, color, colorKind } = resolveInlineStyle(ts, ctx);
|
|
1533
1822
|
switch (tag) {
|
|
1534
1823
|
case "plain_text":
|
|
1535
1824
|
return {
|
|
1536
1825
|
content: typeof prop.content === "string" ? prop.content : "",
|
|
1537
1826
|
bold,
|
|
1538
|
-
color
|
|
1827
|
+
color,
|
|
1828
|
+
colorKind
|
|
1539
1829
|
};
|
|
1540
1830
|
case "code_span":
|
|
1541
1831
|
return {
|
|
1542
1832
|
content: `\`${typeof prop.content === "string" ? prop.content : ""}\``,
|
|
1543
1833
|
bold,
|
|
1544
|
-
color
|
|
1834
|
+
color,
|
|
1835
|
+
colorKind
|
|
1545
1836
|
};
|
|
1546
1837
|
case "link": {
|
|
1547
1838
|
const text = typeof prop.content === "string" ? prop.content : "";
|
|
1548
1839
|
const urlObj = isRecord2(prop.url) ? prop.url : void 0;
|
|
1549
1840
|
const url = urlObj && typeof urlObj.url === "string" ? urlObj.url : "";
|
|
1550
|
-
return { content: `[${text}](${url})`, bold, color };
|
|
1841
|
+
return { content: `[${text}](${url})`, bold, color, colorKind };
|
|
1551
1842
|
}
|
|
1552
1843
|
case "at_all":
|
|
1553
1844
|
return { content: "<at id=all></at>", bold, color };
|
|
@@ -1587,9 +1878,8 @@ function childToMarkdown(child, ctx) {
|
|
|
1587
1878
|
case "plain_text": {
|
|
1588
1879
|
const content = typeof prop.content === "string" ? prop.content : "";
|
|
1589
1880
|
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
return wrapEmphasis(content, attrs.includes("bold"), color);
|
|
1881
|
+
const { bold, color, colorKind } = resolveInlineStyle(ts, ctx);
|
|
1882
|
+
return wrapEmphasis(content, bold, color, colorKind, colorKind === "custom");
|
|
1593
1883
|
}
|
|
1594
1884
|
case "br":
|
|
1595
1885
|
return "\n";
|
|
@@ -1715,7 +2005,7 @@ ${"#".repeat(level)} ${body}
|
|
|
1715
2005
|
if (!data || data.tag !== "markdown") return "";
|
|
1716
2006
|
const cellProp = isRecord2(data.property) ? data.property : {};
|
|
1717
2007
|
const inner = Array.isArray(cellProp.elements) ? cellProp.elements : [];
|
|
1718
|
-
const text = renderChildren(inner, ctx);
|
|
2008
|
+
const { content: text } = renderChildren(inner, ctx);
|
|
1719
2009
|
return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
1720
2010
|
});
|
|
1721
2011
|
return `| ${cells.join(" | ")} |`;
|
|
@@ -1838,10 +2128,10 @@ function rawToDsl(raw) {
|
|
|
1838
2128
|
}
|
|
1839
2129
|
}
|
|
1840
2130
|
const attachment = isRecord2(raw.json_attachment) ? raw.json_attachment : {};
|
|
1841
|
-
const ctx = {
|
|
2131
|
+
const ctx = makeCtx({
|
|
1842
2132
|
images: isRecord2(attachment.images) ? attachment.images : {},
|
|
1843
2133
|
atUsers: isRecord2(attachment.at_users) ? attachment.at_users : {}
|
|
1844
|
-
};
|
|
2134
|
+
});
|
|
1845
2135
|
return convertRoot(inner, ctx);
|
|
1846
2136
|
}
|
|
1847
2137
|
if (isRecord2(raw.card)) {
|
|
@@ -1849,10 +2139,10 @@ function rawToDsl(raw) {
|
|
|
1849
2139
|
if (typeof card.json_card === "string" || isRecord2(card.json_card)) {
|
|
1850
2140
|
return rawToDsl(card);
|
|
1851
2141
|
}
|
|
1852
|
-
return convertRoot(card,
|
|
2142
|
+
return convertRoot(card, makeCtx());
|
|
1853
2143
|
}
|
|
1854
2144
|
if ("body" in raw || "header" in raw || "schema" in raw) {
|
|
1855
|
-
return convertRoot(raw,
|
|
2145
|
+
return convertRoot(raw, makeCtx());
|
|
1856
2146
|
}
|
|
1857
2147
|
return raw;
|
|
1858
2148
|
}
|
|
@@ -1861,109 +2151,18 @@ function degradeUnknown(el) {
|
|
|
1861
2151
|
return { tag: "unknown", raw_tag: rawTag, fallback: el };
|
|
1862
2152
|
}
|
|
1863
2153
|
|
|
1864
|
-
|
|
1865
|
-
function hexColorToRgba(hex) {
|
|
1866
|
-
const h = hex.trim().replace(/^#/, "");
|
|
1867
|
-
if (h.length === 8) {
|
|
1868
|
-
const r = parseInt(h.slice(0, 2), 16);
|
|
1869
|
-
const g = parseInt(h.slice(2, 4), 16);
|
|
1870
|
-
const b = parseInt(h.slice(4, 6), 16);
|
|
1871
|
-
const a = Math.round(parseInt(h.slice(6, 8), 16) / 255 * 100) / 100;
|
|
1872
|
-
return `rgba(${r},${g},${b},${a})`;
|
|
1873
|
-
}
|
|
1874
|
-
if (h.length === 6) {
|
|
1875
|
-
const r = parseInt(h.slice(0, 2), 16);
|
|
1876
|
-
const g = parseInt(h.slice(2, 4), 16);
|
|
1877
|
-
const b = parseInt(h.slice(4, 6), 16);
|
|
1878
|
-
return `rgba(${r},${g},${b},1)`;
|
|
1879
|
-
}
|
|
1880
|
-
return void 0;
|
|
1881
|
-
}
|
|
1882
|
-
function readCustomBackgroundStyle(node) {
|
|
1883
|
-
const raw = node.custom_background_style;
|
|
1884
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
1885
|
-
const o = raw;
|
|
1886
|
-
const lightRaw = typeof o.light_mode === "string" ? o.light_mode : void 0;
|
|
1887
|
-
const darkRaw = typeof o.dark_mode === "string" ? o.dark_mode : void 0;
|
|
1888
|
-
if (!lightRaw || !darkRaw) return void 0;
|
|
1889
|
-
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
1890
|
-
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
1891
|
-
if (!light || !dark) return void 0;
|
|
1892
|
-
return { light_mode: light, dark_mode: dark };
|
|
1893
|
-
}
|
|
1894
|
-
function normalizeStoredCardColors(dsl) {
|
|
1895
|
-
const colorEntries = {};
|
|
1896
|
-
const styleKey = (entry) => `${entry.light_mode}|${entry.dark_mode}`;
|
|
1897
|
-
const keyToAlias = /* @__PURE__ */ new Map();
|
|
1898
|
-
let bgSeq = 0;
|
|
1899
|
-
const ensureAlias = (prefix, entry) => {
|
|
1900
|
-
const existing2 = keyToAlias.get(styleKey(entry));
|
|
1901
|
-
if (existing2) return existing2;
|
|
1902
|
-
const alias = `${prefix}-${bgSeq++}`;
|
|
1903
|
-
colorEntries[alias] = entry;
|
|
1904
|
-
keyToAlias.set(styleKey(entry), alias);
|
|
1905
|
-
return alias;
|
|
1906
|
-
};
|
|
1907
|
-
const walk = (node) => {
|
|
1908
|
-
if (Array.isArray(node)) {
|
|
1909
|
-
for (const child of node) walk(child);
|
|
1910
|
-
return;
|
|
1911
|
-
}
|
|
1912
|
-
if (!node || typeof node !== "object") return;
|
|
1913
|
-
const obj = node;
|
|
1914
|
-
if (obj.tag === "column") {
|
|
1915
|
-
const custom = readCustomBackgroundStyle(obj);
|
|
1916
|
-
if (custom) {
|
|
1917
|
-
obj.background_style = ensureAlias("cus-bg", custom);
|
|
1918
|
-
delete obj.custom_background_style;
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
for (const value of Object.values(obj)) walk(value);
|
|
1922
|
-
};
|
|
1923
|
-
walk(dsl.body);
|
|
1924
|
-
if (Object.keys(colorEntries).length === 0) return;
|
|
1925
|
-
const firstBg = colorEntries["cus-bg-0"] ?? Object.values(colorEntries)[0];
|
|
1926
|
-
if (firstBg) {
|
|
1927
|
-
const m = firstBg.light_mode.match(/^rgba\((\d+),(\d+),(\d+),/);
|
|
1928
|
-
if (m) {
|
|
1929
|
-
colorEntries["cus-accent"] = {
|
|
1930
|
-
light_mode: `rgba(${m[1]},${m[2]},${m[3]},1)`,
|
|
1931
|
-
dark_mode: firstBg.dark_mode.replace(/,\s*[\d.]+\)$/, ",1)")
|
|
1932
|
-
};
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
colorEntries["cus-muted"] ??= {
|
|
1936
|
-
light_mode: "rgba(100,106,115,1)",
|
|
1937
|
-
dark_mode: "rgba(166,166,166,1)"
|
|
1938
|
-
};
|
|
1939
|
-
const config = dsl.config && typeof dsl.config === "object" ? dsl.config : {};
|
|
1940
|
-
dsl.config = config;
|
|
1941
|
-
const style = config.style && typeof config.style === "object" ? config.style : {};
|
|
1942
|
-
config.style = style;
|
|
1943
|
-
const existing = style.color && typeof style.color === "object" ? style.color : {};
|
|
1944
|
-
style.color = { ...colorEntries, ...existing };
|
|
1945
|
-
if (!colorEntries["cus-accent"]) return;
|
|
1946
|
-
const applyDefaultFontColors = (node) => {
|
|
1947
|
-
if (Array.isArray(node)) {
|
|
1948
|
-
for (const child of node) applyDefaultFontColors(child);
|
|
1949
|
-
return;
|
|
1950
|
-
}
|
|
1951
|
-
if (!node || typeof node !== "object") return;
|
|
1952
|
-
const obj = node;
|
|
1953
|
-
if (obj.tag === "markdown" && obj.font_color === void 0) {
|
|
1954
|
-
const size = obj.text_size;
|
|
1955
|
-
const content = typeof obj.content === "string" ? obj.content : "";
|
|
1956
|
-
if (size === "small") obj.font_color = "cus-muted";
|
|
1957
|
-
else if (size === "heading" || content.includes("**")) obj.font_color = "cus-accent";
|
|
1958
|
-
}
|
|
1959
|
-
for (const value of Object.values(obj)) applyDefaultFontColors(value);
|
|
1960
|
-
};
|
|
1961
|
-
applyDefaultFontColors(dsl.body);
|
|
1962
|
-
}
|
|
1963
|
-
|
|
2154
|
+
exports.createColorRegistry = createColorRegistry;
|
|
1964
2155
|
exports.degradeUnknown = degradeUnknown;
|
|
1965
2156
|
exports.dslToRaw = dslToRaw;
|
|
2157
|
+
exports.isCustomHexColor = isCustomHexColor;
|
|
1966
2158
|
exports.isRawCardEnvelope = isRawCardEnvelope;
|
|
1967
2159
|
exports.normalizeStoredCardColors = normalizeStoredCardColors;
|
|
1968
2160
|
exports.rawCardToDsl = rawCardToDsl;
|
|
1969
2161
|
exports.rawToDsl = rawToDsl;
|
|
2162
|
+
exports.readThemeColorDark = readThemeColorDark;
|
|
2163
|
+
exports.readThemeColorLight = readThemeColorLight;
|
|
2164
|
+
exports.registerColorEntry = registerColorEntry;
|
|
2165
|
+
exports.resolveCustomBackgroundLight = resolveCustomBackgroundLight;
|
|
2166
|
+
exports.resolveTextStyleColor = resolveTextStyleColor;
|
|
2167
|
+
exports.resolveTextStyleCustomColorPair = resolveTextStyleCustomColorPair;
|
|
2168
|
+
exports.themePairToColorEntry = themePairToColorEntry;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { dslToRaw } from './dsl-to-raw.js';
|
|
10
10
|
export { rawToDsl, rawCardToDsl, isRawCardEnvelope, degradeUnknown, } from './raw-to-dsl.js';
|
|
11
|
-
export { normalizeStoredCardColors } from './normalize-stored-colors.js';
|
|
11
|
+
export { normalizeStoredCardColors, createColorRegistry, registerColorEntry } from './normalize-stored-colors.js';
|
|
12
12
|
export type { CustomColorEntry } from './normalize-stored-colors.js';
|
|
13
|
+
export { readThemeColorLight, readThemeColorDark, resolveCustomBackgroundLight, resolveTextStyleColor, resolveTextStyleCustomColorPair, themePairToColorEntry, isCustomHexColor, } from './custom-colors.js';
|
|
14
|
+
export type { ThemeColorPair } from './custom-colors.js';
|
|
13
15
|
export type { CardElement, ElementProperty, JsonCard } from './types.js';
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClH,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,+BAA+B,EAC/B,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -866,6 +866,256 @@ function isRecord(v) {
|
|
|
866
866
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
867
867
|
}
|
|
868
868
|
|
|
869
|
+
// src/normalize-stored-colors.ts
|
|
870
|
+
function createColorRegistry() {
|
|
871
|
+
return { entries: {}, keyToAlias: /* @__PURE__ */ new Map(), bgSeq: 0, textSeq: 0 };
|
|
872
|
+
}
|
|
873
|
+
function colorEntryKey(entry) {
|
|
874
|
+
return `${entry.light_mode}|${entry.dark_mode}`;
|
|
875
|
+
}
|
|
876
|
+
function registerColorEntry(registry, entry, prefix) {
|
|
877
|
+
const key = colorEntryKey(entry);
|
|
878
|
+
const existing = registry.keyToAlias.get(key);
|
|
879
|
+
if (existing) return existing;
|
|
880
|
+
const alias = prefix === "cus-bg" ? `cus-bg-${registry.bgSeq++}` : `cus-text-${registry.textSeq++}`;
|
|
881
|
+
registry.entries[alias] = entry;
|
|
882
|
+
registry.keyToAlias.set(key, alias);
|
|
883
|
+
return alias;
|
|
884
|
+
}
|
|
885
|
+
function hexColorToRgba(hex) {
|
|
886
|
+
const h = hex.trim().replace(/^#/, "");
|
|
887
|
+
if (h.length === 8) {
|
|
888
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
889
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
890
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
891
|
+
const a = Math.round(parseInt(h.slice(6, 8), 16) / 255 * 100) / 100;
|
|
892
|
+
return `rgba(${r},${g},${b},${a})`;
|
|
893
|
+
}
|
|
894
|
+
if (h.length === 6) {
|
|
895
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
896
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
897
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
898
|
+
return `rgba(${r},${g},${b},1)`;
|
|
899
|
+
}
|
|
900
|
+
return void 0;
|
|
901
|
+
}
|
|
902
|
+
function readCustomBackgroundStyle(node) {
|
|
903
|
+
const raw = node.custom_background_style;
|
|
904
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
905
|
+
const o = raw;
|
|
906
|
+
const lightRaw = typeof o.light_mode === "string" ? o.light_mode : void 0;
|
|
907
|
+
const darkRaw = typeof o.dark_mode === "string" ? o.dark_mode : void 0;
|
|
908
|
+
if (!lightRaw || !darkRaw) return void 0;
|
|
909
|
+
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
910
|
+
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
911
|
+
if (!light || !dark) return void 0;
|
|
912
|
+
return { light_mode: light, dark_mode: dark };
|
|
913
|
+
}
|
|
914
|
+
function normalizeStoredCardColors(dsl, preRegistered) {
|
|
915
|
+
const colorEntries = { ...preRegistered?.entries ?? {} };
|
|
916
|
+
const keyToAlias = new Map(preRegistered?.keyToAlias ?? []);
|
|
917
|
+
let bgSeq = preRegistered?.bgSeq ?? 0;
|
|
918
|
+
let textSeq = preRegistered?.textSeq ?? 0;
|
|
919
|
+
const ensureAlias = (prefix, entry) => {
|
|
920
|
+
const existing2 = keyToAlias.get(colorEntryKey(entry));
|
|
921
|
+
if (existing2) return existing2;
|
|
922
|
+
const alias = prefix === "cus-bg" ? `cus-bg-${bgSeq++}` : `cus-text-${textSeq++}`;
|
|
923
|
+
colorEntries[alias] = entry;
|
|
924
|
+
keyToAlias.set(colorEntryKey(entry), alias);
|
|
925
|
+
return alias;
|
|
926
|
+
};
|
|
927
|
+
const walk = (node) => {
|
|
928
|
+
if (Array.isArray(node)) {
|
|
929
|
+
for (const child of node) walk(child);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (!node || typeof node !== "object") return;
|
|
933
|
+
const obj = node;
|
|
934
|
+
if (obj.tag === "column") {
|
|
935
|
+
const custom = readCustomBackgroundStyle(obj);
|
|
936
|
+
if (custom) {
|
|
937
|
+
obj.background_style = ensureAlias("cus-bg", custom);
|
|
938
|
+
delete obj.custom_background_style;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (obj.tag === "markdown" && typeof obj.content === "string") {
|
|
942
|
+
normalizeMarkdownInlineHexColors(obj, ensureAlias);
|
|
943
|
+
}
|
|
944
|
+
for (const value of Object.values(obj)) walk(value);
|
|
945
|
+
};
|
|
946
|
+
walk(dsl.body);
|
|
947
|
+
if (Object.keys(colorEntries).length === 0) return;
|
|
948
|
+
const firstBgKey = Object.keys(colorEntries).find((k) => k.startsWith("cus-bg-"));
|
|
949
|
+
const firstBg = firstBgKey ? colorEntries[firstBgKey] : void 0;
|
|
950
|
+
if (firstBg) {
|
|
951
|
+
const m = firstBg.light_mode.match(/^rgba\((\d+),(\d+),(\d+),/);
|
|
952
|
+
if (m) {
|
|
953
|
+
colorEntries["cus-accent"] = {
|
|
954
|
+
light_mode: `rgba(${m[1]},${m[2]},${m[3]},1)`,
|
|
955
|
+
dark_mode: firstBg.dark_mode.replace(/,\s*[\d.]+\)$/, ",1)")
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
colorEntries["cus-muted"] ??= {
|
|
960
|
+
light_mode: "rgba(100,106,115,1)",
|
|
961
|
+
dark_mode: "rgba(166,166,166,1)"
|
|
962
|
+
};
|
|
963
|
+
const aliasRenames = /* @__PURE__ */ new Map();
|
|
964
|
+
for (const to of ["cus-accent", "cus-muted"]) {
|
|
965
|
+
for (const [from, target] of unifyMatchingAlias(colorEntries, "cus-text", to)) {
|
|
966
|
+
aliasRenames.set(from, target);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
remapColorReferences(dsl.body, aliasRenames);
|
|
970
|
+
const config = dsl.config && typeof dsl.config === "object" ? dsl.config : {};
|
|
971
|
+
dsl.config = config;
|
|
972
|
+
const style = config.style && typeof config.style === "object" ? config.style : {};
|
|
973
|
+
config.style = style;
|
|
974
|
+
const existing = style.color && typeof style.color === "object" ? style.color : {};
|
|
975
|
+
style.color = { ...colorEntries, ...existing };
|
|
976
|
+
if (!colorEntries["cus-accent"]) return;
|
|
977
|
+
const applyDefaultFontColors = (node) => {
|
|
978
|
+
if (Array.isArray(node)) {
|
|
979
|
+
for (const child of node) applyDefaultFontColors(child);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (!node || typeof node !== "object") return;
|
|
983
|
+
const obj = node;
|
|
984
|
+
if (obj.tag === "markdown" && obj.font_color === void 0) {
|
|
985
|
+
const size = obj.text_size;
|
|
986
|
+
const content = typeof obj.content === "string" ? obj.content : "";
|
|
987
|
+
if (size === "small") obj.font_color = "cus-muted";
|
|
988
|
+
else if (size === "heading" || content.includes("**")) obj.font_color = "cus-accent";
|
|
989
|
+
}
|
|
990
|
+
for (const value of Object.values(obj)) applyDefaultFontColors(value);
|
|
991
|
+
};
|
|
992
|
+
applyDefaultFontColors(dsl.body);
|
|
993
|
+
}
|
|
994
|
+
function unifyMatchingAlias(entries, fromPrefix, to) {
|
|
995
|
+
const renames = /* @__PURE__ */ new Map();
|
|
996
|
+
const target = entries[to];
|
|
997
|
+
if (!target) return renames;
|
|
998
|
+
for (const alias of Object.keys(entries)) {
|
|
999
|
+
if (alias === to) continue;
|
|
1000
|
+
if (!alias.startsWith(`${fromPrefix}-`) && alias !== fromPrefix) continue;
|
|
1001
|
+
const entry = entries[alias];
|
|
1002
|
+
const matches = to === "cus-accent" || to === "cus-muted" ? entry.light_mode === target.light_mode : colorEntryKey(entry) === colorEntryKey(target);
|
|
1003
|
+
if (matches) {
|
|
1004
|
+
renames.set(alias, to);
|
|
1005
|
+
delete entries[alias];
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return renames;
|
|
1009
|
+
}
|
|
1010
|
+
function remapColorReferences(node, aliasRenames) {
|
|
1011
|
+
const remap = (alias) => {
|
|
1012
|
+
if (!alias) return alias;
|
|
1013
|
+
return aliasRenames.get(alias) ?? alias;
|
|
1014
|
+
};
|
|
1015
|
+
const walk = (n) => {
|
|
1016
|
+
if (Array.isArray(n)) {
|
|
1017
|
+
for (const c of n) walk(c);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (!n || typeof n !== "object") return;
|
|
1021
|
+
const obj = n;
|
|
1022
|
+
if (typeof obj.font_color === "string") obj.font_color = remap(obj.font_color);
|
|
1023
|
+
if (typeof obj.content === "string" && obj.content.includes("<font")) {
|
|
1024
|
+
obj.content = obj.content.replace(
|
|
1025
|
+
/<font color=['"]([^'"]+)['"]>/g,
|
|
1026
|
+
(_m, color) => {
|
|
1027
|
+
const next = remap(color);
|
|
1028
|
+
return next ? `<font color='${next}'>` : _m;
|
|
1029
|
+
}
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
for (const value of Object.values(obj)) walk(value);
|
|
1033
|
+
};
|
|
1034
|
+
walk(node);
|
|
1035
|
+
}
|
|
1036
|
+
function normalizeMarkdownInlineHexColors(obj, ensureAlias) {
|
|
1037
|
+
const content = obj.content;
|
|
1038
|
+
const hexColor = (raw) => {
|
|
1039
|
+
const v = raw.trim();
|
|
1040
|
+
if (v.startsWith("#")) {
|
|
1041
|
+
const rgba = hexColorToRgba(v);
|
|
1042
|
+
return rgba ? { light_mode: rgba, dark_mode: rgba } : void 0;
|
|
1043
|
+
}
|
|
1044
|
+
if (/^rgba?\(/i.test(v)) return { light_mode: v, dark_mode: v };
|
|
1045
|
+
return void 0;
|
|
1046
|
+
};
|
|
1047
|
+
const single = content.match(/^<font color=['"]([^'"]+)['"]\s*>([\s\S]*)<\/font>$/);
|
|
1048
|
+
if (single) {
|
|
1049
|
+
const entry = hexColor(single[1]);
|
|
1050
|
+
if (entry) {
|
|
1051
|
+
const alias = ensureAlias("cus-text", entry);
|
|
1052
|
+
if (obj.font_color === void 0) obj.font_color = alias;
|
|
1053
|
+
obj.content = single[2];
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
obj.content = content.replace(/<font color=['"]([^'"]+)['"]>/g, (match, raw) => {
|
|
1058
|
+
const entry = hexColor(raw);
|
|
1059
|
+
if (!entry) return match;
|
|
1060
|
+
const alias = ensureAlias("cus-text", entry);
|
|
1061
|
+
return `<font color='${alias}'>`;
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// src/custom-colors.ts
|
|
1066
|
+
function readThemeColorLight(pair) {
|
|
1067
|
+
if (!pair || typeof pair !== "object") return void 0;
|
|
1068
|
+
const o = pair;
|
|
1069
|
+
const raw = o.light_mode ?? o.lightMode;
|
|
1070
|
+
if (typeof raw !== "string") return void 0;
|
|
1071
|
+
const v = raw.trim();
|
|
1072
|
+
return v || void 0;
|
|
1073
|
+
}
|
|
1074
|
+
function readThemeColorDark(pair) {
|
|
1075
|
+
if (!pair || typeof pair !== "object") return void 0;
|
|
1076
|
+
const o = pair;
|
|
1077
|
+
const raw = o.dark_mode ?? o.darkMode;
|
|
1078
|
+
if (typeof raw !== "string") return void 0;
|
|
1079
|
+
const v = raw.trim();
|
|
1080
|
+
return v || void 0;
|
|
1081
|
+
}
|
|
1082
|
+
function isCustomHexColor(color) {
|
|
1083
|
+
const v = color.trim();
|
|
1084
|
+
return v.startsWith("#") || /^rgba?\(/i.test(v);
|
|
1085
|
+
}
|
|
1086
|
+
function resolveTextStyleColor(textStyle) {
|
|
1087
|
+
if (!textStyle || typeof textStyle !== "object") return void 0;
|
|
1088
|
+
const ts = textStyle;
|
|
1089
|
+
const enumColor = typeof ts.color === "string" ? ts.color.trim() : void 0;
|
|
1090
|
+
if (enumColor && enumColor !== "default") return enumColor;
|
|
1091
|
+
const custom = ts.custom_color ?? ts.customColor;
|
|
1092
|
+
return readThemeColorLight(custom);
|
|
1093
|
+
}
|
|
1094
|
+
function resolveTextStyleCustomColorPair(textStyle) {
|
|
1095
|
+
if (!textStyle || typeof textStyle !== "object") return void 0;
|
|
1096
|
+
const ts = textStyle;
|
|
1097
|
+
const enumColor = typeof ts.color === "string" ? ts.color.trim() : void 0;
|
|
1098
|
+
if (enumColor && enumColor !== "default") return void 0;
|
|
1099
|
+
const custom = ts.custom_color ?? ts.customColor;
|
|
1100
|
+
if (!custom || typeof custom !== "object") return void 0;
|
|
1101
|
+
return custom;
|
|
1102
|
+
}
|
|
1103
|
+
function themePairToColorEntry(pair) {
|
|
1104
|
+
const lightRaw = readThemeColorLight(pair);
|
|
1105
|
+
const darkRaw = readThemeColorDark(pair);
|
|
1106
|
+
if (!lightRaw || !darkRaw) return void 0;
|
|
1107
|
+
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
1108
|
+
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
1109
|
+
if (!light || !dark) return void 0;
|
|
1110
|
+
return { light_mode: light, dark_mode: dark };
|
|
1111
|
+
}
|
|
1112
|
+
function resolveCustomBackgroundLight(property) {
|
|
1113
|
+
if (!property || typeof property !== "object") return void 0;
|
|
1114
|
+
const p = property;
|
|
1115
|
+
const custom = p.custom_background_style ?? p.customBackgroundStyle;
|
|
1116
|
+
return readThemeColorLight(custom);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
869
1119
|
// src/raw-to-dsl.ts
|
|
870
1120
|
function isRawCardEnvelope(value) {
|
|
871
1121
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -881,12 +1131,19 @@ function rawCardToDsl(envelope) {
|
|
|
881
1131
|
} catch {
|
|
882
1132
|
return envelope;
|
|
883
1133
|
}
|
|
884
|
-
const ctx = {
|
|
1134
|
+
const ctx = makeCtx({
|
|
885
1135
|
images: env.json_attachment?.images ?? {},
|
|
886
1136
|
atUsers: env.json_attachment?.at_users ?? {}
|
|
887
|
-
};
|
|
1137
|
+
});
|
|
888
1138
|
return convertRoot(inner, ctx);
|
|
889
1139
|
}
|
|
1140
|
+
function makeCtx(partial) {
|
|
1141
|
+
return {
|
|
1142
|
+
images: partial?.images ?? {},
|
|
1143
|
+
atUsers: partial?.atUsers ?? {},
|
|
1144
|
+
colors: createColorRegistry()
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
890
1147
|
var RAW_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
891
1148
|
"new_body",
|
|
892
1149
|
"source",
|
|
@@ -974,6 +1231,7 @@ function convertRoot(root, ctx) {
|
|
|
974
1231
|
out.config = convertConfig(isRecord2(root.config) ? root.config : {});
|
|
975
1232
|
if (isRecord2(root.body)) out.body = convertBody2(root.body, ctx);
|
|
976
1233
|
if (isRecord2(root.header)) out.header = convertHeader2(root.header, ctx);
|
|
1234
|
+
normalizeStoredCardColors(out, ctx.colors);
|
|
977
1235
|
return out;
|
|
978
1236
|
}
|
|
979
1237
|
function convertConfig(_node) {
|
|
@@ -1075,7 +1333,7 @@ function unwrap(node, ctx) {
|
|
|
1075
1333
|
let value = isRecord2(cell) && "data" in cell ? cell.data : cell;
|
|
1076
1334
|
if (isRecord2(value) && value.tag === "markdown" && isRecord2(value.property)) {
|
|
1077
1335
|
const elements = Array.isArray(value.property.elements) ? value.property.elements : [];
|
|
1078
|
-
value = renderChildren(elements, ctx);
|
|
1336
|
+
value = renderChildren(elements, ctx).content;
|
|
1079
1337
|
}
|
|
1080
1338
|
r[k] = value;
|
|
1081
1339
|
}
|
|
@@ -1450,8 +1708,9 @@ function resolveAtUserId(prop, ctx) {
|
|
|
1450
1708
|
function rebuildMarkdown(property, ctx) {
|
|
1451
1709
|
const out = { tag: "markdown" };
|
|
1452
1710
|
const elements = Array.isArray(property.elements) ? property.elements : [];
|
|
1453
|
-
const content = renderChildren(elements, ctx);
|
|
1711
|
+
const { content, uniformFontColor } = renderChildren(elements, ctx);
|
|
1454
1712
|
if (content) out.content = content;
|
|
1713
|
+
if (uniformFontColor) out.font_color = uniformFontColor;
|
|
1455
1714
|
for (const [rawKey, rawValue] of Object.entries(property)) {
|
|
1456
1715
|
if (rawKey === "elements" || rawKey === "markdownElements" || rawKey === "originTag") continue;
|
|
1457
1716
|
if (rawKey === "udIcon") continue;
|
|
@@ -1469,7 +1728,7 @@ function rebuildMarkdown(property, ctx) {
|
|
|
1469
1728
|
}
|
|
1470
1729
|
return out;
|
|
1471
1730
|
}
|
|
1472
|
-
function wrapEmphasis(text, bold, color) {
|
|
1731
|
+
function wrapEmphasis(text, bold, color, colorKind, inlineCustomFont = false) {
|
|
1473
1732
|
if (!bold && !color) return text;
|
|
1474
1733
|
const lead = text.match(/^\s*/)[0];
|
|
1475
1734
|
const trail = text.match(/\s*$/)[0];
|
|
@@ -1477,20 +1736,51 @@ function wrapEmphasis(text, bold, color) {
|
|
|
1477
1736
|
if (core.length === 0) return text;
|
|
1478
1737
|
let wrapped = core;
|
|
1479
1738
|
if (bold) wrapped = `**${wrapped}**`;
|
|
1480
|
-
if (color)
|
|
1739
|
+
if (color) {
|
|
1740
|
+
if (colorKind === "enum") wrapped = `<font color='${color}'>${wrapped}</font>`;
|
|
1741
|
+
else if (colorKind === "custom" && inlineCustomFont) wrapped = `<font color='${color}'>${wrapped}</font>`;
|
|
1742
|
+
}
|
|
1481
1743
|
return lead + wrapped + trail;
|
|
1482
1744
|
}
|
|
1745
|
+
function resolveInlineStyle(ts, ctx) {
|
|
1746
|
+
const bold = !!(ts && Array.isArray(ts.attributes) && ts.attributes.includes("bold"));
|
|
1747
|
+
const customPair = resolveTextStyleCustomColorPair(ts);
|
|
1748
|
+
if (customPair) {
|
|
1749
|
+
const entry = themePairToColorEntry(customPair);
|
|
1750
|
+
if (entry) {
|
|
1751
|
+
const alias = registerColorEntry(ctx.colors, entry, "cus-text");
|
|
1752
|
+
return { bold, color: alias, colorKind: "custom" };
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
const rawColor = resolveTextStyleColor(ts);
|
|
1756
|
+
const color = rawColor === "default" ? void 0 : rawColor;
|
|
1757
|
+
if (!color) return { bold };
|
|
1758
|
+
if (isCustomHexColor(color)) {
|
|
1759
|
+
const rgba = color.startsWith("#") ? themePairToColorEntry({ lightMode: color, darkMode: color }) : { light_mode: color, dark_mode: color };
|
|
1760
|
+
if (rgba) {
|
|
1761
|
+
const alias = registerColorEntry(ctx.colors, rgba, "cus-text");
|
|
1762
|
+
return { bold, color: alias, colorKind: "custom" };
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return { bold, color, colorKind: "enum" };
|
|
1766
|
+
}
|
|
1483
1767
|
function renderChildren(children, ctx) {
|
|
1484
1768
|
let out = "";
|
|
1485
1769
|
let buffer = [];
|
|
1770
|
+
let uniformFontColor;
|
|
1486
1771
|
function flushBuffer() {
|
|
1487
1772
|
if (buffer.length === 0) return;
|
|
1773
|
+
const customColors = buffer.filter((s) => s.colorKind === "custom" && s.color).map((s) => s.color);
|
|
1774
|
+
const hasEnumColor = buffer.some((s) => s.colorKind === "enum" && s.color);
|
|
1775
|
+
const uniformCustom = customColors.length > 0 && !hasEnumColor && customColors.every((c) => c === customColors[0]) ? customColors[0] : void 0;
|
|
1776
|
+
if (uniformCustom) uniformFontColor = uniformCustom;
|
|
1488
1777
|
let group = [];
|
|
1489
1778
|
function emitGroup() {
|
|
1490
1779
|
if (group.length === 0) return;
|
|
1491
1780
|
const inner = group.map((s) => s.content).join("");
|
|
1492
|
-
const { bold, color } = group[0];
|
|
1493
|
-
|
|
1781
|
+
const { bold, color, colorKind } = group[0];
|
|
1782
|
+
const inlineCustomFont = colorKind === "custom" && !!color && color !== uniformCustom;
|
|
1783
|
+
out += wrapEmphasis(inner, bold, color, colorKind, inlineCustomFont);
|
|
1494
1784
|
group = [];
|
|
1495
1785
|
}
|
|
1496
1786
|
for (const seg of buffer) {
|
|
@@ -1499,8 +1789,9 @@ function renderChildren(children, ctx) {
|
|
|
1499
1789
|
continue;
|
|
1500
1790
|
}
|
|
1501
1791
|
const head = group[0];
|
|
1502
|
-
if (head.bold === seg.bold && head.color === seg.color
|
|
1503
|
-
|
|
1792
|
+
if (head.bold === seg.bold && head.color === seg.color && head.colorKind === seg.colorKind) {
|
|
1793
|
+
group.push(seg);
|
|
1794
|
+
} else {
|
|
1504
1795
|
emitGroup();
|
|
1505
1796
|
group.push(seg);
|
|
1506
1797
|
}
|
|
@@ -1519,33 +1810,33 @@ function renderChildren(children, ctx) {
|
|
|
1519
1810
|
out += childToMarkdown(child, ctx);
|
|
1520
1811
|
}
|
|
1521
1812
|
flushBuffer();
|
|
1522
|
-
return out;
|
|
1813
|
+
return { content: out, uniformFontColor };
|
|
1523
1814
|
}
|
|
1524
1815
|
function childToInlineSegment(child, ctx) {
|
|
1525
1816
|
const tag = child.tag;
|
|
1526
1817
|
const prop = isRecord2(child.property) ? child.property : {};
|
|
1527
1818
|
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1528
|
-
const bold
|
|
1529
|
-
const rawColor = ts && typeof ts.color === "string" ? ts.color : void 0;
|
|
1530
|
-
const color = rawColor === "default" ? void 0 : rawColor;
|
|
1819
|
+
const { bold, color, colorKind } = resolveInlineStyle(ts, ctx);
|
|
1531
1820
|
switch (tag) {
|
|
1532
1821
|
case "plain_text":
|
|
1533
1822
|
return {
|
|
1534
1823
|
content: typeof prop.content === "string" ? prop.content : "",
|
|
1535
1824
|
bold,
|
|
1536
|
-
color
|
|
1825
|
+
color,
|
|
1826
|
+
colorKind
|
|
1537
1827
|
};
|
|
1538
1828
|
case "code_span":
|
|
1539
1829
|
return {
|
|
1540
1830
|
content: `\`${typeof prop.content === "string" ? prop.content : ""}\``,
|
|
1541
1831
|
bold,
|
|
1542
|
-
color
|
|
1832
|
+
color,
|
|
1833
|
+
colorKind
|
|
1543
1834
|
};
|
|
1544
1835
|
case "link": {
|
|
1545
1836
|
const text = typeof prop.content === "string" ? prop.content : "";
|
|
1546
1837
|
const urlObj = isRecord2(prop.url) ? prop.url : void 0;
|
|
1547
1838
|
const url = urlObj && typeof urlObj.url === "string" ? urlObj.url : "";
|
|
1548
|
-
return { content: `[${text}](${url})`, bold, color };
|
|
1839
|
+
return { content: `[${text}](${url})`, bold, color, colorKind };
|
|
1549
1840
|
}
|
|
1550
1841
|
case "at_all":
|
|
1551
1842
|
return { content: "<at id=all></at>", bold, color };
|
|
@@ -1585,9 +1876,8 @@ function childToMarkdown(child, ctx) {
|
|
|
1585
1876
|
case "plain_text": {
|
|
1586
1877
|
const content = typeof prop.content === "string" ? prop.content : "";
|
|
1587
1878
|
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
return wrapEmphasis(content, attrs.includes("bold"), color);
|
|
1879
|
+
const { bold, color, colorKind } = resolveInlineStyle(ts, ctx);
|
|
1880
|
+
return wrapEmphasis(content, bold, color, colorKind, colorKind === "custom");
|
|
1591
1881
|
}
|
|
1592
1882
|
case "br":
|
|
1593
1883
|
return "\n";
|
|
@@ -1713,7 +2003,7 @@ ${"#".repeat(level)} ${body}
|
|
|
1713
2003
|
if (!data || data.tag !== "markdown") return "";
|
|
1714
2004
|
const cellProp = isRecord2(data.property) ? data.property : {};
|
|
1715
2005
|
const inner = Array.isArray(cellProp.elements) ? cellProp.elements : [];
|
|
1716
|
-
const text = renderChildren(inner, ctx);
|
|
2006
|
+
const { content: text } = renderChildren(inner, ctx);
|
|
1717
2007
|
return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
1718
2008
|
});
|
|
1719
2009
|
return `| ${cells.join(" | ")} |`;
|
|
@@ -1836,10 +2126,10 @@ function rawToDsl(raw) {
|
|
|
1836
2126
|
}
|
|
1837
2127
|
}
|
|
1838
2128
|
const attachment = isRecord2(raw.json_attachment) ? raw.json_attachment : {};
|
|
1839
|
-
const ctx = {
|
|
2129
|
+
const ctx = makeCtx({
|
|
1840
2130
|
images: isRecord2(attachment.images) ? attachment.images : {},
|
|
1841
2131
|
atUsers: isRecord2(attachment.at_users) ? attachment.at_users : {}
|
|
1842
|
-
};
|
|
2132
|
+
});
|
|
1843
2133
|
return convertRoot(inner, ctx);
|
|
1844
2134
|
}
|
|
1845
2135
|
if (isRecord2(raw.card)) {
|
|
@@ -1847,10 +2137,10 @@ function rawToDsl(raw) {
|
|
|
1847
2137
|
if (typeof card.json_card === "string" || isRecord2(card.json_card)) {
|
|
1848
2138
|
return rawToDsl(card);
|
|
1849
2139
|
}
|
|
1850
|
-
return convertRoot(card,
|
|
2140
|
+
return convertRoot(card, makeCtx());
|
|
1851
2141
|
}
|
|
1852
2142
|
if ("body" in raw || "header" in raw || "schema" in raw) {
|
|
1853
|
-
return convertRoot(raw,
|
|
2143
|
+
return convertRoot(raw, makeCtx());
|
|
1854
2144
|
}
|
|
1855
2145
|
return raw;
|
|
1856
2146
|
}
|
|
@@ -1859,104 +2149,4 @@ function degradeUnknown(el) {
|
|
|
1859
2149
|
return { tag: "unknown", raw_tag: rawTag, fallback: el };
|
|
1860
2150
|
}
|
|
1861
2151
|
|
|
1862
|
-
|
|
1863
|
-
function hexColorToRgba(hex) {
|
|
1864
|
-
const h = hex.trim().replace(/^#/, "");
|
|
1865
|
-
if (h.length === 8) {
|
|
1866
|
-
const r = parseInt(h.slice(0, 2), 16);
|
|
1867
|
-
const g = parseInt(h.slice(2, 4), 16);
|
|
1868
|
-
const b = parseInt(h.slice(4, 6), 16);
|
|
1869
|
-
const a = Math.round(parseInt(h.slice(6, 8), 16) / 255 * 100) / 100;
|
|
1870
|
-
return `rgba(${r},${g},${b},${a})`;
|
|
1871
|
-
}
|
|
1872
|
-
if (h.length === 6) {
|
|
1873
|
-
const r = parseInt(h.slice(0, 2), 16);
|
|
1874
|
-
const g = parseInt(h.slice(2, 4), 16);
|
|
1875
|
-
const b = parseInt(h.slice(4, 6), 16);
|
|
1876
|
-
return `rgba(${r},${g},${b},1)`;
|
|
1877
|
-
}
|
|
1878
|
-
return void 0;
|
|
1879
|
-
}
|
|
1880
|
-
function readCustomBackgroundStyle(node) {
|
|
1881
|
-
const raw = node.custom_background_style;
|
|
1882
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
1883
|
-
const o = raw;
|
|
1884
|
-
const lightRaw = typeof o.light_mode === "string" ? o.light_mode : void 0;
|
|
1885
|
-
const darkRaw = typeof o.dark_mode === "string" ? o.dark_mode : void 0;
|
|
1886
|
-
if (!lightRaw || !darkRaw) return void 0;
|
|
1887
|
-
const light = lightRaw.startsWith("#") ? hexColorToRgba(lightRaw) : lightRaw;
|
|
1888
|
-
const dark = darkRaw.startsWith("#") ? hexColorToRgba(darkRaw) : darkRaw;
|
|
1889
|
-
if (!light || !dark) return void 0;
|
|
1890
|
-
return { light_mode: light, dark_mode: dark };
|
|
1891
|
-
}
|
|
1892
|
-
function normalizeStoredCardColors(dsl) {
|
|
1893
|
-
const colorEntries = {};
|
|
1894
|
-
const styleKey = (entry) => `${entry.light_mode}|${entry.dark_mode}`;
|
|
1895
|
-
const keyToAlias = /* @__PURE__ */ new Map();
|
|
1896
|
-
let bgSeq = 0;
|
|
1897
|
-
const ensureAlias = (prefix, entry) => {
|
|
1898
|
-
const existing2 = keyToAlias.get(styleKey(entry));
|
|
1899
|
-
if (existing2) return existing2;
|
|
1900
|
-
const alias = `${prefix}-${bgSeq++}`;
|
|
1901
|
-
colorEntries[alias] = entry;
|
|
1902
|
-
keyToAlias.set(styleKey(entry), alias);
|
|
1903
|
-
return alias;
|
|
1904
|
-
};
|
|
1905
|
-
const walk = (node) => {
|
|
1906
|
-
if (Array.isArray(node)) {
|
|
1907
|
-
for (const child of node) walk(child);
|
|
1908
|
-
return;
|
|
1909
|
-
}
|
|
1910
|
-
if (!node || typeof node !== "object") return;
|
|
1911
|
-
const obj = node;
|
|
1912
|
-
if (obj.tag === "column") {
|
|
1913
|
-
const custom = readCustomBackgroundStyle(obj);
|
|
1914
|
-
if (custom) {
|
|
1915
|
-
obj.background_style = ensureAlias("cus-bg", custom);
|
|
1916
|
-
delete obj.custom_background_style;
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
for (const value of Object.values(obj)) walk(value);
|
|
1920
|
-
};
|
|
1921
|
-
walk(dsl.body);
|
|
1922
|
-
if (Object.keys(colorEntries).length === 0) return;
|
|
1923
|
-
const firstBg = colorEntries["cus-bg-0"] ?? Object.values(colorEntries)[0];
|
|
1924
|
-
if (firstBg) {
|
|
1925
|
-
const m = firstBg.light_mode.match(/^rgba\((\d+),(\d+),(\d+),/);
|
|
1926
|
-
if (m) {
|
|
1927
|
-
colorEntries["cus-accent"] = {
|
|
1928
|
-
light_mode: `rgba(${m[1]},${m[2]},${m[3]},1)`,
|
|
1929
|
-
dark_mode: firstBg.dark_mode.replace(/,\s*[\d.]+\)$/, ",1)")
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
colorEntries["cus-muted"] ??= {
|
|
1934
|
-
light_mode: "rgba(100,106,115,1)",
|
|
1935
|
-
dark_mode: "rgba(166,166,166,1)"
|
|
1936
|
-
};
|
|
1937
|
-
const config = dsl.config && typeof dsl.config === "object" ? dsl.config : {};
|
|
1938
|
-
dsl.config = config;
|
|
1939
|
-
const style = config.style && typeof config.style === "object" ? config.style : {};
|
|
1940
|
-
config.style = style;
|
|
1941
|
-
const existing = style.color && typeof style.color === "object" ? style.color : {};
|
|
1942
|
-
style.color = { ...colorEntries, ...existing };
|
|
1943
|
-
if (!colorEntries["cus-accent"]) return;
|
|
1944
|
-
const applyDefaultFontColors = (node) => {
|
|
1945
|
-
if (Array.isArray(node)) {
|
|
1946
|
-
for (const child of node) applyDefaultFontColors(child);
|
|
1947
|
-
return;
|
|
1948
|
-
}
|
|
1949
|
-
if (!node || typeof node !== "object") return;
|
|
1950
|
-
const obj = node;
|
|
1951
|
-
if (obj.tag === "markdown" && obj.font_color === void 0) {
|
|
1952
|
-
const size = obj.text_size;
|
|
1953
|
-
const content = typeof obj.content === "string" ? obj.content : "";
|
|
1954
|
-
if (size === "small") obj.font_color = "cus-muted";
|
|
1955
|
-
else if (size === "heading" || content.includes("**")) obj.font_color = "cus-accent";
|
|
1956
|
-
}
|
|
1957
|
-
for (const value of Object.values(obj)) applyDefaultFontColors(value);
|
|
1958
|
-
};
|
|
1959
|
-
applyDefaultFontColors(dsl.body);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
export { degradeUnknown, dslToRaw, isRawCardEnvelope, normalizeStoredCardColors, rawCardToDsl, rawToDsl };
|
|
2152
|
+
export { createColorRegistry, degradeUnknown, dslToRaw, isCustomHexColor, isRawCardEnvelope, normalizeStoredCardColors, rawCardToDsl, rawToDsl, readThemeColorDark, readThemeColorLight, registerColorEntry, resolveCustomBackgroundLight, resolveTextStyleColor, resolveTextStyleCustomColorPair, themePairToColorEntry };
|
|
@@ -6,9 +6,20 @@ export type CustomColorEntry = {
|
|
|
6
6
|
light_mode: string;
|
|
7
7
|
dark_mode: string;
|
|
8
8
|
};
|
|
9
|
+
export type ColorRegistry = {
|
|
10
|
+
entries: Record<string, CustomColorEntry>;
|
|
11
|
+
keyToAlias: Map<string, string>;
|
|
12
|
+
bgSeq: number;
|
|
13
|
+
textSeq: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function createColorRegistry(): ColorRegistry;
|
|
16
|
+
export declare function colorEntryKey(entry: CustomColorEntry): string;
|
|
17
|
+
export declare function registerColorEntry(registry: ColorRegistry, entry: CustomColorEntry, prefix: 'cus-bg' | 'cus-text'): string;
|
|
18
|
+
export declare function hexColorToRgba(hex: string): string | undefined;
|
|
9
19
|
/**
|
|
10
20
|
* 将飞书回读的 custom_background_style 还原为可再次发送的 background_style cus-N + config.style.color。
|
|
11
21
|
* 若 markdown 缺少 font_color,按 text_size / 加粗内容补 cus-accent / cus-muted(飞书回读会丢 font_color)。
|
|
22
|
+
* rawToDsl 可传入已登记的文字自定义色(preRegistered),与背景色一并写入 config.style.color。
|
|
12
23
|
*/
|
|
13
|
-
export declare function normalizeStoredCardColors(dsl: Record<string, unknown
|
|
24
|
+
export declare function normalizeStoredCardColors(dsl: Record<string, unknown>, preRegistered?: ColorRegistry): void;
|
|
14
25
|
//# sourceMappingURL=normalize-stored-colors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-stored-colors.d.ts","sourceRoot":"","sources":["../src/normalize-stored-colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"normalize-stored-colors.d.ts","sourceRoot":"","sources":["../src/normalize-stored-colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC1C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,mBAAmB,IAAI,aAAa,CAEnD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAE7D;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAC5B,MAAM,CAUR;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB9D;AAiBD;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,aAAa,CAAC,EAAE,aAAa,GAC5B,IAAI,CAiGN"}
|
package/dist/raw-to-dsl.d.ts
CHANGED
|
@@ -56,7 +56,8 @@ export declare function rawToDsl(raw: unknown): unknown;
|
|
|
56
56
|
/**
|
|
57
57
|
* Optional helper: wrap a private/unknown element as
|
|
58
58
|
* `{ tag: 'unknown', raw_tag, fallback }`.
|
|
59
|
-
* Not applied automatically by `
|
|
59
|
+
* Not applied automatically by `degradeUnknown` (parity with upstream feishu-raw-card-to-dsl).
|
|
60
|
+
* `convertRoot` / `rawCardToDsl` always run `normalizeStoredCardColors` on output.
|
|
60
61
|
*/
|
|
61
62
|
export declare function degradeUnknown(el: Record<string, unknown>): Record<string, unknown>;
|
|
62
63
|
//# sourceMappingURL=raw-to-dsl.d.ts.map
|
package/dist/raw-to-dsl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-to-dsl.d.ts","sourceRoot":"","sources":["../src/raw-to-dsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;
|
|
1
|
+
{"version":3,"file":"raw-to-dsl.d.ts","sourceRoot":"","sources":["../src/raw-to-dsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAwBH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIzD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAcvD;AAwkDD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CA8C9C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGnF"}
|