@cortexkit/opencode-magic-context 0.29.1 → 0.30.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 +148 -66
- package/dist/shared/announcement.d.ts +1 -1
- package/dist/shared/announcement.d.ts.map +1 -1
- package/dist/tui/badge-contrast.d.ts +37 -22
- package/dist/tui/badge-contrast.d.ts.map +1 -1
- package/package.json +78 -78
- package/src/shared/announcement.ts +4 -3
- package/src/tui/badge-contrast.test.ts +39 -1
- package/src/tui/badge-contrast.ts +63 -25
- package/src/tui/slots/sidebar-content.tsx +2 -2
package/dist/index.js
CHANGED
|
@@ -163278,7 +163278,7 @@ var require_src = __commonJS((exports, module) => {
|
|
|
163278
163278
|
};
|
|
163279
163279
|
});
|
|
163280
163280
|
|
|
163281
|
-
// ../../node_modules/.bun/comment-json@
|
|
163281
|
+
// ../../node_modules/.bun/comment-json@5.0.0/node_modules/comment-json/src/common.js
|
|
163282
163282
|
var require_common = __commonJS((exports, module) => {
|
|
163283
163283
|
var PREFIX_BEFORE = "before";
|
|
163284
163284
|
var PREFIX_AFTER_PROP = "after-prop";
|
|
@@ -163310,8 +163310,6 @@ var require_common = __commonJS((exports, module) => {
|
|
|
163310
163310
|
var NON_PROP_SYMBOL_KEYS = NON_PROP_SYMBOL_PREFIXES.map(Symbol.for);
|
|
163311
163311
|
var COLON = ":";
|
|
163312
163312
|
var UNDEFINED = undefined;
|
|
163313
|
-
var LINE_BREAKS_BEFORE = new WeakMap;
|
|
163314
|
-
var LINE_BREAKS_AFTER = new WeakMap;
|
|
163315
163313
|
var RAW_STRING_LITERALS = new WeakMap;
|
|
163316
163314
|
var is_string = (subject) => typeof subject === "string";
|
|
163317
163315
|
var is_number = (subject) => typeof subject === "number";
|
|
@@ -163414,17 +163412,49 @@ var require_common = __commonJS((exports, module) => {
|
|
|
163414
163412
|
});
|
|
163415
163413
|
return target;
|
|
163416
163414
|
};
|
|
163417
|
-
var
|
|
163418
|
-
|
|
163419
|
-
|
|
163420
|
-
|
|
163415
|
+
var is_comment_symbol = (subject) => {
|
|
163416
|
+
const key = Symbol.keyFor(subject);
|
|
163417
|
+
return is_string(key) && (NON_PROP_SYMBOL_PREFIXES.includes(key) || PROP_SYMBOL_PREFIXES.some((prefix) => key.indexOf(prefix + COLON) === 0));
|
|
163418
|
+
};
|
|
163419
|
+
var remove_blank_line_tokens = (comments) => {
|
|
163420
|
+
let write = 0;
|
|
163421
|
+
let removed = false;
|
|
163422
|
+
comments.forEach((comment) => {
|
|
163423
|
+
if (comment && comment.type === "BlankLine") {
|
|
163424
|
+
removed = true;
|
|
163425
|
+
return;
|
|
163426
|
+
}
|
|
163427
|
+
comments[write++] = comment;
|
|
163428
|
+
});
|
|
163429
|
+
comments.length = write;
|
|
163430
|
+
return removed;
|
|
163431
|
+
};
|
|
163432
|
+
var remove_blank_lines_from_prop = (target, prop) => {
|
|
163433
|
+
if (!Object.hasOwn(target, prop)) {
|
|
163434
|
+
return;
|
|
163421
163435
|
}
|
|
163422
|
-
|
|
163423
|
-
|
|
163436
|
+
const comments = target[prop];
|
|
163437
|
+
if (!Array.isArray(comments) || !remove_blank_line_tokens(comments)) {
|
|
163438
|
+
return;
|
|
163439
|
+
}
|
|
163440
|
+
if (!comments.length) {
|
|
163441
|
+
delete target[prop];
|
|
163424
163442
|
}
|
|
163425
163443
|
};
|
|
163426
|
-
var
|
|
163427
|
-
|
|
163444
|
+
var remove_blank_lines_deep = (target) => {
|
|
163445
|
+
Object.getOwnPropertySymbols(target).forEach((prop) => {
|
|
163446
|
+
if (is_comment_symbol(prop)) {
|
|
163447
|
+
remove_blank_lines_from_prop(target, prop);
|
|
163448
|
+
}
|
|
163449
|
+
});
|
|
163450
|
+
Object.keys(target).forEach((key) => {
|
|
163451
|
+
const value = target[key];
|
|
163452
|
+
if (is_object(value)) {
|
|
163453
|
+
remove_blank_lines_deep(value);
|
|
163454
|
+
}
|
|
163455
|
+
});
|
|
163456
|
+
};
|
|
163457
|
+
var is_raw_json = typeof JSON.isRawJSON === "function" ? JSON.isRawJSON : () => false;
|
|
163428
163458
|
module.exports = {
|
|
163429
163459
|
PROP_SYMBOL_PREFIXES,
|
|
163430
163460
|
PREFIX_BEFORE,
|
|
@@ -163454,9 +163484,6 @@ var require_common = __commonJS((exports, module) => {
|
|
|
163454
163484
|
is_raw_json,
|
|
163455
163485
|
set_raw_string_literal,
|
|
163456
163486
|
get_raw_string_literal,
|
|
163457
|
-
set_comment_line_breaks,
|
|
163458
|
-
get_comment_line_breaks_before,
|
|
163459
|
-
get_comment_line_breaks_after,
|
|
163460
163487
|
assign(target, source, keys) {
|
|
163461
163488
|
if (!is_object(target)) {
|
|
163462
163489
|
throw new TypeError("Cannot convert undefined or null to object");
|
|
@@ -163518,11 +163545,26 @@ var require_common = __commonJS((exports, module) => {
|
|
|
163518
163545
|
return;
|
|
163519
163546
|
}
|
|
163520
163547
|
delete target[prop];
|
|
163548
|
+
},
|
|
163549
|
+
removeBlankLines(target, location) {
|
|
163550
|
+
if (!is_object(target)) {
|
|
163551
|
+
throw new TypeError("target must be an object");
|
|
163552
|
+
}
|
|
163553
|
+
if (location === UNDEFINED) {
|
|
163554
|
+
remove_blank_lines_deep(target);
|
|
163555
|
+
return;
|
|
163556
|
+
}
|
|
163557
|
+
const {
|
|
163558
|
+
where,
|
|
163559
|
+
key
|
|
163560
|
+
} = location;
|
|
163561
|
+
const prop = symbol_checked(where, key);
|
|
163562
|
+
remove_blank_lines_from_prop(target, prop);
|
|
163521
163563
|
}
|
|
163522
163564
|
};
|
|
163523
163565
|
});
|
|
163524
163566
|
|
|
163525
|
-
// ../../node_modules/.bun/comment-json@
|
|
163567
|
+
// ../../node_modules/.bun/comment-json@5.0.0/node_modules/comment-json/src/array.js
|
|
163526
163568
|
var require_array = __commonJS((exports, module) => {
|
|
163527
163569
|
var { sort } = require_src();
|
|
163528
163570
|
var {
|
|
@@ -163680,7 +163722,7 @@ var require_array = __commonJS((exports, module) => {
|
|
|
163680
163722
|
};
|
|
163681
163723
|
});
|
|
163682
163724
|
|
|
163683
|
-
// ../../node_modules/.bun/comment-json@
|
|
163725
|
+
// ../../node_modules/.bun/comment-json@5.0.0/node_modules/comment-json/src/parse.js
|
|
163684
163726
|
var require_parse = __commonJS((exports, module) => {
|
|
163685
163727
|
var esprima = require_esprima();
|
|
163686
163728
|
var {
|
|
@@ -163706,7 +163748,6 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
163706
163748
|
is_object,
|
|
163707
163749
|
define: define2,
|
|
163708
163750
|
set_raw_string_literal,
|
|
163709
|
-
set_comment_line_breaks,
|
|
163710
163751
|
assign_non_prop_comments
|
|
163711
163752
|
} = require_common();
|
|
163712
163753
|
var tokenize = (code) => esprima.tokenize(code, {
|
|
@@ -163720,23 +163761,27 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
163720
163761
|
var previous_props = [];
|
|
163721
163762
|
var last_prop;
|
|
163722
163763
|
var remove_comments = false;
|
|
163764
|
+
var remove_blank_lines = false;
|
|
163723
163765
|
var inline = false;
|
|
163724
163766
|
var tokens = null;
|
|
163725
163767
|
var last = null;
|
|
163726
163768
|
var current = null;
|
|
163727
163769
|
var index;
|
|
163728
163770
|
var reviver = null;
|
|
163771
|
+
var source_line_count = 0;
|
|
163729
163772
|
var clean = () => {
|
|
163730
163773
|
current_code = UNDEFINED;
|
|
163731
163774
|
previous_props.length = previous_hosts.length = 0;
|
|
163732
163775
|
last = null;
|
|
163733
163776
|
last_prop = UNDEFINED;
|
|
163777
|
+
remove_blank_lines = false;
|
|
163734
163778
|
};
|
|
163735
163779
|
var free = () => {
|
|
163736
163780
|
clean();
|
|
163737
163781
|
tokens.length = 0;
|
|
163738
163782
|
unassigned_comments = comments_host = tokens = last = current = reviver = null;
|
|
163739
163783
|
current_code = UNDEFINED;
|
|
163784
|
+
source_line_count = 0;
|
|
163740
163785
|
};
|
|
163741
163786
|
var symbolFor = (prefix) => Symbol.for(last_prop !== UNDEFINED ? prefix + COLON + last_prop : prefix);
|
|
163742
163787
|
var transform2 = (k, { value, context = {} }) => reviver ? reviver(k, value, context) : value;
|
|
@@ -163780,6 +163825,37 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
163780
163825
|
var restore_comments_host = () => {
|
|
163781
163826
|
comments_host = previous_hosts.pop();
|
|
163782
163827
|
};
|
|
163828
|
+
var create_blank_line = () => ({
|
|
163829
|
+
type: "BlankLine",
|
|
163830
|
+
inline: false
|
|
163831
|
+
});
|
|
163832
|
+
var append_blank_lines = (comments, from_line, to_line) => {
|
|
163833
|
+
if (remove_blank_lines) {
|
|
163834
|
+
return;
|
|
163835
|
+
}
|
|
163836
|
+
let blank_lines = Math.max(0, to_line - from_line - 1);
|
|
163837
|
+
while (blank_lines-- > 0) {
|
|
163838
|
+
comments.push(create_blank_line());
|
|
163839
|
+
}
|
|
163840
|
+
};
|
|
163841
|
+
var normalize_parse_options = (options) => {
|
|
163842
|
+
if (typeof options === "boolean") {
|
|
163843
|
+
return {
|
|
163844
|
+
no_comments: options,
|
|
163845
|
+
no_blank_lines: false
|
|
163846
|
+
};
|
|
163847
|
+
}
|
|
163848
|
+
if (!is_object(options)) {
|
|
163849
|
+
return {
|
|
163850
|
+
no_comments: false,
|
|
163851
|
+
no_blank_lines: false
|
|
163852
|
+
};
|
|
163853
|
+
}
|
|
163854
|
+
return {
|
|
163855
|
+
no_comments: !!options.no_comments,
|
|
163856
|
+
no_blank_lines: !!options.no_blank_lines
|
|
163857
|
+
};
|
|
163858
|
+
};
|
|
163783
163859
|
var assign_after_comments = () => {
|
|
163784
163860
|
if (!unassigned_comments) {
|
|
163785
163861
|
return;
|
|
@@ -163812,26 +163888,26 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
163812
163888
|
};
|
|
163813
163889
|
var parse_comments = (prefix) => {
|
|
163814
163890
|
const comments = [];
|
|
163891
|
+
let previous_line = last ? last.loc.end.line : 0;
|
|
163815
163892
|
while (current && (is("LineComment") || is("BlockComment"))) {
|
|
163893
|
+
append_blank_lines(comments, previous_line, current.loc.start.line);
|
|
163816
163894
|
const comment = {
|
|
163817
163895
|
...current,
|
|
163818
163896
|
inline
|
|
163819
163897
|
};
|
|
163820
|
-
const previous_line = last ? last.loc.end.line : 1;
|
|
163821
|
-
set_comment_line_breaks(comment, Math.max(0, comment.loc.start.line - previous_line));
|
|
163822
163898
|
comments.push(comment);
|
|
163899
|
+
previous_line = comment.loc.end.line;
|
|
163823
163900
|
next();
|
|
163824
163901
|
}
|
|
163825
|
-
|
|
163826
|
-
if (length) {
|
|
163827
|
-
const comment = comments[length - 1];
|
|
163828
|
-
const current_line = current ? current.loc.start.line : comment.loc.end.line;
|
|
163829
|
-
set_comment_line_breaks(comment, undefined, Math.max(0, current_line - comment.loc.end.line));
|
|
163830
|
-
}
|
|
163902
|
+
append_blank_lines(comments, previous_line, current ? current.loc.start.line : source_line_count);
|
|
163831
163903
|
if (remove_comments) {
|
|
163832
|
-
|
|
163904
|
+
for (let i = comments.length - 1;i >= 0; i--) {
|
|
163905
|
+
if (comments[i].type !== "BlankLine") {
|
|
163906
|
+
comments.splice(i, 1);
|
|
163907
|
+
}
|
|
163908
|
+
}
|
|
163833
163909
|
}
|
|
163834
|
-
if (!length) {
|
|
163910
|
+
if (!comments.length) {
|
|
163835
163911
|
return;
|
|
163836
163912
|
}
|
|
163837
163913
|
if (prefix) {
|
|
@@ -163970,12 +164046,19 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
163970
164046
|
return {};
|
|
163971
164047
|
}
|
|
163972
164048
|
}
|
|
163973
|
-
var parse5 = (code, rev,
|
|
164049
|
+
var parse5 = (code, rev, options) => {
|
|
163974
164050
|
clean();
|
|
164051
|
+
const {
|
|
164052
|
+
no_comments,
|
|
164053
|
+
no_blank_lines
|
|
164054
|
+
} = normalize_parse_options(options);
|
|
163975
164055
|
current_code = code;
|
|
164056
|
+
source_line_count = code.split(`
|
|
164057
|
+
`).length;
|
|
163976
164058
|
tokens = tokenize(code);
|
|
163977
164059
|
reviver = rev;
|
|
163978
164060
|
remove_comments = no_comments;
|
|
164061
|
+
remove_blank_lines = no_blank_lines;
|
|
163979
164062
|
if (!tokens.length) {
|
|
163980
164063
|
unexpected_end();
|
|
163981
164064
|
}
|
|
@@ -164005,7 +164088,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
164005
164088
|
};
|
|
164006
164089
|
});
|
|
164007
164090
|
|
|
164008
|
-
// ../../node_modules/.bun/comment-json@
|
|
164091
|
+
// ../../node_modules/.bun/comment-json@5.0.0/node_modules/comment-json/src/stringify.js
|
|
164009
164092
|
var require_stringify = __commonJS((exports, module) => {
|
|
164010
164093
|
var {
|
|
164011
164094
|
PREFIX_BEFORE_ALL,
|
|
@@ -164027,8 +164110,6 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164027
164110
|
is_number,
|
|
164028
164111
|
is_object,
|
|
164029
164112
|
get_raw_string_literal,
|
|
164030
|
-
get_comment_line_breaks_before,
|
|
164031
|
-
get_comment_line_breaks_after,
|
|
164032
164113
|
is_raw_json
|
|
164033
164114
|
} = require_common();
|
|
164034
164115
|
var SPACE = " ";
|
|
@@ -164043,26 +164124,18 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164043
164124
|
var quote = JSON.stringify;
|
|
164044
164125
|
var comment_stringify = (value, line) => line ? `//${value}` : `/*${value}*/`;
|
|
164045
164126
|
var repeat_line_breaks = (line_breaks, gap) => (LF + gap).repeat(line_breaks);
|
|
164046
|
-
var
|
|
164047
|
-
var
|
|
164048
|
-
if (!previous_comment || !previous_comment.loc || !comment.loc) {
|
|
164049
|
-
return null;
|
|
164050
|
-
}
|
|
164051
|
-
const { end } = previous_comment.loc;
|
|
164052
|
-
const { start } = comment.loc;
|
|
164053
|
-
if (!end || !start || !is_number(end.line) || !is_number(start.line)) {
|
|
164054
|
-
return null;
|
|
164055
|
-
}
|
|
164056
|
-
const line_breaks = start.line - end.line;
|
|
164057
|
-
return line_breaks >= 0 ? line_breaks : null;
|
|
164058
|
-
};
|
|
164059
|
-
var count_trailing_line_breaks = (str, gap) => {
|
|
164060
|
-
const unit = LF + gap;
|
|
164061
|
-
const { length } = unit;
|
|
164127
|
+
var is_inline_whitespace = (char) => char === SPACE || char === "\t";
|
|
164128
|
+
var count_trailing_line_breaks = (str) => {
|
|
164062
164129
|
let i = str.length;
|
|
164063
164130
|
let count = 0;
|
|
164064
|
-
while (i
|
|
164065
|
-
i
|
|
164131
|
+
while (i > 0) {
|
|
164132
|
+
while (i > 0 && is_inline_whitespace(str[i - 1])) {
|
|
164133
|
+
i--;
|
|
164134
|
+
}
|
|
164135
|
+
if (i === 0 || str[i - 1] !== LF) {
|
|
164136
|
+
return count;
|
|
164137
|
+
}
|
|
164138
|
+
i--;
|
|
164066
164139
|
count++;
|
|
164067
164140
|
}
|
|
164068
164141
|
return count;
|
|
@@ -164073,27 +164146,30 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164073
164146
|
return EMPTY;
|
|
164074
164147
|
}
|
|
164075
164148
|
let str = EMPTY;
|
|
164149
|
+
let blank_lines = 0;
|
|
164076
164150
|
let last_comment = null;
|
|
164077
164151
|
comments.forEach((comment, i) => {
|
|
164152
|
+
if (comment.type === "BlankLine") {
|
|
164153
|
+
blank_lines++;
|
|
164154
|
+
return;
|
|
164155
|
+
}
|
|
164078
164156
|
const {
|
|
164079
164157
|
inline,
|
|
164080
164158
|
type,
|
|
164081
164159
|
value
|
|
164082
164160
|
} = comment;
|
|
164083
|
-
|
|
164084
|
-
|
|
164085
|
-
line_breaks_before = read_line_breaks_from_loc(last_comment, comment);
|
|
164086
|
-
}
|
|
164087
|
-
if (line_breaks_before === null) {
|
|
164088
|
-
line_breaks_before = inline ? 0 : 1;
|
|
164089
|
-
}
|
|
164090
|
-
const delimiter = line_breaks_before > 0 ? repeat_line_breaks(line_breaks_before, deeper_gap) : inline ? SPACE : i === 0 ? EMPTY : LF + deeper_gap;
|
|
164161
|
+
const line_breaks_before = blank_lines > 0 ? blank_lines + (inline ? 0 : 1) : null;
|
|
164162
|
+
const delimiter = line_breaks_before > 0 ? repeat_line_breaks(line_breaks_before, deeper_gap) : inline ? SPACE : i === 0 && symbol_tag === PREFIX_BEFORE_ALL && deeper_gap === EMPTY ? EMPTY : LF + deeper_gap;
|
|
164091
164163
|
const is_line_comment = type === "LineComment";
|
|
164092
164164
|
str += delimiter + comment_stringify(value, is_line_comment);
|
|
164165
|
+
blank_lines = 0;
|
|
164093
164166
|
last_comment = comment;
|
|
164094
164167
|
});
|
|
164168
|
+
if (!last_comment) {
|
|
164169
|
+
return blank_lines ? repeat_line_breaks(blank_lines + 1, deeper_gap) : EMPTY;
|
|
164170
|
+
}
|
|
164095
164171
|
const default_line_breaks_after = display_block || last_comment.type === "LineComment" ? 1 : 0;
|
|
164096
|
-
const line_breaks_after =
|
|
164172
|
+
const line_breaks_after = blank_lines > 0 ? blank_lines + 1 : default_line_breaks_after;
|
|
164097
164173
|
return str + repeat_line_breaks(line_breaks_after, deeper_gap);
|
|
164098
164174
|
};
|
|
164099
164175
|
var replacer = null;
|
|
@@ -164102,7 +164178,7 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164102
164178
|
replacer = null;
|
|
164103
164179
|
indent = EMPTY;
|
|
164104
164180
|
};
|
|
164105
|
-
var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one
|
|
164181
|
+
var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two)), gap) : EMPTY;
|
|
164106
164182
|
var join_content = (inside, value, gap) => {
|
|
164107
164183
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
164108
164184
|
return join6(comment, inside, gap);
|
|
@@ -164201,6 +164277,8 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164201
164277
|
const str = toString.call(subject);
|
|
164202
164278
|
return PRIMITIVE_OBJECT_TYPES.includes(str);
|
|
164203
164279
|
};
|
|
164280
|
+
var normalize_blank_lines = (str) => typeof str === "string" ? str.replace(/\n[ \t]+(?=\n)/g, `
|
|
164281
|
+
`) : str;
|
|
164204
164282
|
module.exports = (value, replacer_, space) => {
|
|
164205
164283
|
const indent_ = get_indent(space);
|
|
164206
164284
|
if (!indent_) {
|
|
@@ -164213,11 +164291,12 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
164213
164291
|
indent = indent_;
|
|
164214
164292
|
const str = is_primitive_object(value) ? JSON.stringify(value) : stringify("", { "": value }, EMPTY);
|
|
164215
164293
|
clean();
|
|
164216
|
-
|
|
164294
|
+
const output = is_object(value) ? process_comments(value, PREFIX_BEFORE_ALL, EMPTY, true).trimLeft() + str + process_comments(value, PREFIX_AFTER_ALL, EMPTY).trimRight() : str;
|
|
164295
|
+
return normalize_blank_lines(output);
|
|
164217
164296
|
};
|
|
164218
164297
|
});
|
|
164219
164298
|
|
|
164220
|
-
// ../../node_modules/.bun/comment-json@
|
|
164299
|
+
// ../../node_modules/.bun/comment-json@5.0.0/node_modules/comment-json/src/index.js
|
|
164221
164300
|
var require_src2 = __commonJS((exports, module) => {
|
|
164222
164301
|
var { parse: parse5, tokenize } = require_parse();
|
|
164223
164302
|
var stringify = require_stringify();
|
|
@@ -164232,7 +164311,8 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
164232
164311
|
PREFIX_AFTER_ALL,
|
|
164233
164312
|
assign,
|
|
164234
164313
|
moveComments,
|
|
164235
|
-
removeComments
|
|
164314
|
+
removeComments,
|
|
164315
|
+
removeBlankLines
|
|
164236
164316
|
} = require_common();
|
|
164237
164317
|
module.exports = {
|
|
164238
164318
|
PREFIX_BEFORE,
|
|
@@ -164248,7 +164328,8 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
164248
164328
|
CommentArray,
|
|
164249
164329
|
assign,
|
|
164250
164330
|
moveComments,
|
|
164251
|
-
removeComments
|
|
164331
|
+
removeComments,
|
|
164332
|
+
removeBlankLines
|
|
164252
164333
|
};
|
|
164253
164334
|
});
|
|
164254
164335
|
|
|
@@ -184641,12 +184722,13 @@ function shouldShowAnnouncement() {
|
|
|
184641
184722
|
}
|
|
184642
184723
|
return ordering > 0;
|
|
184643
184724
|
}
|
|
184644
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
184725
|
+
var ANNOUNCEMENT_VERSION = "0.30.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
|
|
184645
184726
|
var init_announcement = __esm(() => {
|
|
184646
184727
|
init_data_path();
|
|
184647
184728
|
ANNOUNCEMENT_FEATURES = [
|
|
184648
|
-
|
|
184649
|
-
"
|
|
184729
|
+
"Setup and doctor now recognize an OpenCode Desktop install that has no CLI on PATH, instead of reporting OpenCode as not installed (#196). Desktop-only users continue setup with manual model entry.",
|
|
184730
|
+
"TUI sidebar badge color follows your theme again (#198): it uses the theme background like sibling badges, with a fallback so the label can't disappear on transparent themes.",
|
|
184731
|
+
"doctor clears a stale plugin cache more reliably (#199): it compares against the plugin's latest published version and preserves the cache when offline."
|
|
184650
184732
|
];
|
|
184651
184733
|
});
|
|
184652
184734
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Bump only when there are user-visible changes worth a startup dialog.
|
|
19
19
|
* Does NOT need to match the published package version.
|
|
20
20
|
*/
|
|
21
|
-
export declare const ANNOUNCEMENT_VERSION = "0.
|
|
21
|
+
export declare const ANNOUNCEMENT_VERSION = "0.30.0";
|
|
22
22
|
/**
|
|
23
23
|
* Short, user-facing bullet strings. Keep each line ~80 chars or shorter so the
|
|
24
24
|
* TUI dialog renders cleanly without horizontal scroll on a typical terminal.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announcement.d.ts","sourceRoot":"","sources":["../../src/shared/announcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"announcement.d.ts","sourceRoot":"","sources":["../../src/shared/announcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAIvD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAyBtF;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAGjD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAS1D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CA0BhD"}
|
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pick
|
|
3
|
-
* background
|
|
2
|
+
* Pick the text color for the sidebar header badge (a bold label drawn on a
|
|
3
|
+
* `theme.accent` background).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* always opaque, so deriving the text color from it is transparency-proof.
|
|
5
|
+
* Primary rule (matches AFT's sidebar by construction): paint the theme's own
|
|
6
|
+
* `background` color as the label, the inverse-of-panel look. Because it is a
|
|
7
|
+
* fixed theme token rather than an accent-derived computation, MC's badge and
|
|
8
|
+
* AFT's badge agree on EVERY accent automatically, so the same theme can never
|
|
9
|
+
* make one badge black and the other white (issue #198).
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
11
|
+
* Fallback rule (the reason a luminance pick exists at all): `theme.background`
|
|
12
|
+
* can be unusable as a label color in two degenerate cases, where it would
|
|
13
|
+
* render the label invisible on the accent:
|
|
14
|
+
* 1. Transparent background. Themes that respect terminal transparency set
|
|
15
|
+
* `background: "none"`, which resolves to `RGBA(0,0,0,0)`; drawing it as
|
|
16
|
+
* text renders fully transparent and the label disappears (issue #186).
|
|
17
|
+
* 2. Background ~= accent. If the theme's background and accent are nearly the
|
|
18
|
+
* same color, background-on-accent text has no contrast.
|
|
19
|
+
* In either case we fall back to a black/white pick that is guaranteed visible
|
|
20
|
+
* on the always-opaque accent.
|
|
21
21
|
*
|
|
22
|
-
* `RGBA` channels from @opentui/core are normalized 0..1 floats
|
|
23
|
-
* minimal `{ r, g, b }` shape so this stays a pure, trivially
|
|
24
|
-
* independent of the native color class
|
|
22
|
+
* `RGBA` channels from @opentui/core are normalized 0..1 floats (alpha included).
|
|
23
|
+
* We accept the minimal `{ r, g, b, a? }` shape so this stays a pure, trivially
|
|
24
|
+
* testable function independent of the native color class, and we return the
|
|
25
|
+
* passed-in `background` object unchanged on the primary path so it stays the
|
|
26
|
+
* exact same theme token AFT uses.
|
|
25
27
|
*/
|
|
26
|
-
|
|
28
|
+
type Color = {
|
|
27
29
|
r: number;
|
|
28
30
|
g: number;
|
|
29
31
|
b: number;
|
|
30
|
-
|
|
32
|
+
a?: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Pure black/white pick by accent luminance. Used as the badge fallback and kept
|
|
36
|
+
* exported for callers that only have the accent.
|
|
37
|
+
*/
|
|
38
|
+
export declare function readableTextColorOn(bg: Color): string;
|
|
39
|
+
/**
|
|
40
|
+
* Badge label color on the accent: the theme background (AFT parity) when it is
|
|
41
|
+
* usable, else a guaranteed-visible black/white fallback. Returns the passed-in
|
|
42
|
+
* `background` reference unchanged on the primary path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function badgeTextColor<T extends Color>(accent: T, background: T): T | string;
|
|
45
|
+
export {};
|
|
31
46
|
//# sourceMappingURL=badge-contrast.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge-contrast.d.ts","sourceRoot":"","sources":["../../src/tui/badge-contrast.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"badge-contrast.d.ts","sourceRoot":"","sources":["../../src/tui/badge-contrast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,KAAK,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAoC7D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CAErD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAMpF"}
|
package/package.json
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
2
|
+
"name": "@cortexkit/opencode-magic-context",
|
|
3
|
+
"version": "0.30.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "OpenCode plugin for Magic Context — cross-session memory and context management",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"author": "ualtinok",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"opencode",
|
|
12
|
+
"plugin",
|
|
13
|
+
"context",
|
|
14
|
+
"memory",
|
|
15
|
+
"ai",
|
|
16
|
+
"llm",
|
|
17
|
+
"prompt-caching",
|
|
18
|
+
"session-history"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/cortexkit/magic-context",
|
|
23
|
+
"directory": "packages/plugin"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src/tui",
|
|
28
|
+
"src/shared",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @huggingface/transformers --external onnxruntime-web --external bun:sqlite --external node:sqlite && tsc --emitDeclarationOnly",
|
|
33
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.scripts.json",
|
|
34
|
+
"test": "bun test",
|
|
35
|
+
"lint": "biome check .",
|
|
36
|
+
"lint:fix": "biome check --write .",
|
|
37
|
+
"format": "biome format --write .",
|
|
38
|
+
"format:check": "biome format .",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"prepublishOnly": "bun run build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@huggingface/transformers": "^4.1.0",
|
|
44
|
+
"@jitl/quickjs-singlefile-cjs-release-asyncify": "0.32.0",
|
|
45
|
+
"@opencode-ai/plugin": "^1.17.11",
|
|
46
|
+
"@opencode-ai/sdk": "^1.17.11",
|
|
47
|
+
"@opentui/core": "^0.4.2",
|
|
48
|
+
"@opentui/solid": "^0.4.2",
|
|
49
|
+
"ai-tokenizer": "^1.0.6",
|
|
50
|
+
"comment-json": "^5.0.0",
|
|
51
|
+
"quickjs-emscripten": "^0.32.0",
|
|
52
|
+
"solid-js": "1.9.12",
|
|
53
|
+
"zod": "^4.1.8"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@biomejs/biome": "^2.5.1",
|
|
57
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
58
|
+
"@types/bun": "^1.3.10",
|
|
59
|
+
"@types/node": "^22.20.0",
|
|
60
|
+
"bun-types": "^1.3.10",
|
|
61
|
+
"typescript": "^5.8.0"
|
|
62
|
+
},
|
|
63
|
+
"exports": {
|
|
64
|
+
".": {
|
|
65
|
+
"types": "./dist/index.d.ts",
|
|
66
|
+
"import": "./dist/index.js"
|
|
67
|
+
},
|
|
68
|
+
"./tui": {
|
|
69
|
+
"types": "./src/tui/index.tsx",
|
|
70
|
+
"import": "./src/tui/index.tsx"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"oc-plugin": [
|
|
74
|
+
"server",
|
|
75
|
+
"tui"
|
|
76
|
+
],
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"@opencode-ai/plugin": ">=1.15.0"
|
|
79
|
+
}
|
|
80
80
|
}
|
|
@@ -24,15 +24,16 @@ import { getMagicContextStorageDir } from "./data-path";
|
|
|
24
24
|
* Bump only when there are user-visible changes worth a startup dialog.
|
|
25
25
|
* Does NOT need to match the published package version.
|
|
26
26
|
*/
|
|
27
|
-
export const ANNOUNCEMENT_VERSION = "0.
|
|
27
|
+
export const ANNOUNCEMENT_VERSION = "0.30.0";
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Short, user-facing bullet strings. Keep each line ~80 chars or shorter so the
|
|
31
31
|
* TUI dialog renders cleanly without horizontal scroll on a typical terminal.
|
|
32
32
|
*/
|
|
33
33
|
export const ANNOUNCEMENT_FEATURES: ReadonlyArray<string> = [
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"Setup and doctor now recognize an OpenCode Desktop install that has no CLI on PATH, instead of reporting OpenCode as not installed (#196). Desktop-only users continue setup with manual model entry.",
|
|
35
|
+
"TUI sidebar badge color follows your theme again (#198): it uses the theme background like sibling badges, with a fallback so the label can't disappear on transparent themes.",
|
|
36
|
+
"doctor clears a stale plugin cache more reliably (#199): it compares against the plugin's latest published version and preserves the cache when offline.",
|
|
36
37
|
];
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { readableTextColorOn } from "./badge-contrast";
|
|
2
|
+
import { badgeTextColor, readableTextColorOn } from "./badge-contrast";
|
|
3
|
+
|
|
4
|
+
describe("badgeTextColor (AFT parity with #186 safety net)", () => {
|
|
5
|
+
// A purple/lavender accent on a dark theme: the badge label should be the
|
|
6
|
+
// theme background (the inverse-of-panel look), the same fixed token the AFT
|
|
7
|
+
// sidebar uses, so two sibling badges never disagree on the same accent.
|
|
8
|
+
const accent = { r: 0.6, g: 0.5, b: 0.9, a: 1 };
|
|
9
|
+
|
|
10
|
+
test("opaque distinct background is used verbatim as the label", () => {
|
|
11
|
+
const background = { r: 0.05, g: 0.05, b: 0.07, a: 1 }; // near-black dark theme
|
|
12
|
+
// Returns the SAME background token rather than a computed color.
|
|
13
|
+
expect(badgeTextColor(accent, background)).toBe(background);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("light theme: background is used verbatim too (near-white label inverse)", () => {
|
|
17
|
+
const background = { r: 0.97, g: 0.97, b: 0.95, a: 1 };
|
|
18
|
+
expect(badgeTextColor(accent, background)).toBe(background);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("transparent background (alpha 0) falls back to a visible pick (#186)", () => {
|
|
22
|
+
// background:"none" resolves to RGBA(0,0,0,0); using it as text would
|
|
23
|
+
// render the label invisible, so fall back to a contrast pick on accent.
|
|
24
|
+
const transparent = { r: 0, g: 0, b: 0, a: 0 };
|
|
25
|
+
const result = badgeTextColor(accent, transparent);
|
|
26
|
+
expect(result).not.toBe(transparent);
|
|
27
|
+
expect(result).toBe(readableTextColorOn(accent));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("background ~= accent falls back to a visible pick", () => {
|
|
31
|
+
const sameAsAccent = { r: 0.6, g: 0.5, b: 0.9, a: 1 };
|
|
32
|
+
const result = badgeTextColor(accent, sameAsAccent);
|
|
33
|
+
expect(result).toBe(readableTextColorOn(accent));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("missing alpha is treated as opaque", () => {
|
|
37
|
+
const background = { r: 0.05, g: 0.05, b: 0.07 };
|
|
38
|
+
expect(badgeTextColor(accent, background)).toBe(background);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
3
41
|
|
|
4
42
|
describe("readableTextColorOn", () => {
|
|
5
43
|
test("dark accent gets white text", () => {
|
|
@@ -1,39 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pick
|
|
3
|
-
* background
|
|
2
|
+
* Pick the text color for the sidebar header badge (a bold label drawn on a
|
|
3
|
+
* `theme.accent` background).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* always opaque, so deriving the text color from it is transparency-proof.
|
|
5
|
+
* Primary rule (matches AFT's sidebar by construction): paint the theme's own
|
|
6
|
+
* `background` color as the label, the inverse-of-panel look. Because it is a
|
|
7
|
+
* fixed theme token rather than an accent-derived computation, MC's badge and
|
|
8
|
+
* AFT's badge agree on EVERY accent automatically, so the same theme can never
|
|
9
|
+
* make one badge black and the other white (issue #198).
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
11
|
+
* Fallback rule (the reason a luminance pick exists at all): `theme.background`
|
|
12
|
+
* can be unusable as a label color in two degenerate cases, where it would
|
|
13
|
+
* render the label invisible on the accent:
|
|
14
|
+
* 1. Transparent background. Themes that respect terminal transparency set
|
|
15
|
+
* `background: "none"`, which resolves to `RGBA(0,0,0,0)`; drawing it as
|
|
16
|
+
* text renders fully transparent and the label disappears (issue #186).
|
|
17
|
+
* 2. Background ~= accent. If the theme's background and accent are nearly the
|
|
18
|
+
* same color, background-on-accent text has no contrast.
|
|
19
|
+
* In either case we fall back to a black/white pick that is guaranteed visible
|
|
20
|
+
* on the always-opaque accent.
|
|
21
21
|
*
|
|
22
|
-
* `RGBA` channels from @opentui/core are normalized 0..1 floats
|
|
23
|
-
* minimal `{ r, g, b }` shape so this stays a pure, trivially
|
|
24
|
-
* independent of the native color class
|
|
22
|
+
* `RGBA` channels from @opentui/core are normalized 0..1 floats (alpha included).
|
|
23
|
+
* We accept the minimal `{ r, g, b, a? }` shape so this stays a pure, trivially
|
|
24
|
+
* testable function independent of the native color class, and we return the
|
|
25
|
+
* passed-in `background` object unchanged on the primary path so it stays the
|
|
26
|
+
* exact same theme token AFT uses.
|
|
25
27
|
*/
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//
|
|
29
|
+
type Color = { r: number; g: number; b: number; a?: number };
|
|
30
|
+
|
|
31
|
+
// Below this alpha the theme background is too transparent to read as a label on
|
|
32
|
+
// the accent (issue #186: background:"none" resolves to alpha 0).
|
|
33
|
+
const MIN_OPAQUE_ALPHA = 0.5;
|
|
34
|
+
|
|
35
|
+
// If the theme background and accent are within this per-channel distance they
|
|
36
|
+
// are effectively the same color, so background-on-accent text is unreadable.
|
|
37
|
+
const MIN_CHANNEL_DISTANCE = 0.06;
|
|
38
|
+
|
|
39
|
+
// Luminance midpoint for the fallback pick: accents below this keep white text,
|
|
40
|
+
// accents at/above it (light/pastel/near-white) get black. White-biased relative
|
|
41
|
+
// to the strict equal-contrast crossover (~0.179) so saturated mid-tone accents
|
|
42
|
+
// stay white. Only consulted on the degenerate fallback path.
|
|
30
43
|
const LIGHT_ACCENT_LUMINANCE = 0.5;
|
|
31
44
|
|
|
32
45
|
function srgbChannelToLinear(c: number): number {
|
|
33
46
|
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
function relativeLuminance(bg:
|
|
49
|
+
function relativeLuminance(bg: Color): number {
|
|
37
50
|
return (
|
|
38
51
|
0.2126 * srgbChannelToLinear(bg.r) +
|
|
39
52
|
0.7152 * srgbChannelToLinear(bg.g) +
|
|
@@ -41,6 +54,31 @@ function relativeLuminance(bg: { r: number; g: number; b: number }): number {
|
|
|
41
54
|
);
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
function nearlyEqual(a: Color, b: Color): boolean {
|
|
58
|
+
return (
|
|
59
|
+
Math.abs(a.r - b.r) < MIN_CHANNEL_DISTANCE &&
|
|
60
|
+
Math.abs(a.g - b.g) < MIN_CHANNEL_DISTANCE &&
|
|
61
|
+
Math.abs(a.b - b.b) < MIN_CHANNEL_DISTANCE
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Pure black/white pick by accent luminance. Used as the badge fallback and kept
|
|
67
|
+
* exported for callers that only have the accent.
|
|
68
|
+
*/
|
|
69
|
+
export function readableTextColorOn(bg: Color): string {
|
|
45
70
|
return relativeLuminance(bg) < LIGHT_ACCENT_LUMINANCE ? "#ffffff" : "#000000";
|
|
46
71
|
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Badge label color on the accent: the theme background (AFT parity) when it is
|
|
75
|
+
* usable, else a guaranteed-visible black/white fallback. Returns the passed-in
|
|
76
|
+
* `background` reference unchanged on the primary path.
|
|
77
|
+
*/
|
|
78
|
+
export function badgeTextColor<T extends Color>(accent: T, background: T): T | string {
|
|
79
|
+
const alpha = background.a ?? 1;
|
|
80
|
+
if (alpha >= MIN_OPAQUE_ALPHA && !nearlyEqual(accent, background)) {
|
|
81
|
+
return background;
|
|
82
|
+
}
|
|
83
|
+
return readableTextColorOn(accent);
|
|
84
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Show, createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
|
3
3
|
import type { TuiSlotPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
4
4
|
import packageJson from "../../../package.json"
|
|
5
|
-
import {
|
|
5
|
+
import { badgeTextColor } from '../badge-contrast';
|
|
6
6
|
import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
|
|
7
7
|
import { formatThresholdPercent } from "../../shared/format-threshold"
|
|
8
8
|
import {
|
|
@@ -695,7 +695,7 @@ const SidebarContent = (props: {
|
|
|
695
695
|
onMouseDown={() => props.controller.toggleCollapsed()}
|
|
696
696
|
>
|
|
697
697
|
<box paddingLeft={1} paddingRight={1} backgroundColor={props.theme.accent}>
|
|
698
|
-
<text fg={
|
|
698
|
+
<text fg={badgeTextColor(props.theme.accent, props.theme.background)}>
|
|
699
699
|
<b>{collapsed() ? "▶ " : "▼ "}{headerLabel()}</b>
|
|
700
700
|
</text>
|
|
701
701
|
</box>
|