Compare commits

...

1 Commits

Author SHA1 Message Date
you
d8c76ca47a fix: customizer v2 type colors default to black (#514)
- Server: add default typeColors in handleConfigTheme matching roles.js
- Client: fall back to window.TYPE_COLORS before #000000 in _renderNodes
- Tests: verify typeColors defaults in theme API and frontend fallback
2026-04-03 05:25:57 +00:00
4 changed files with 54 additions and 2 deletions

View File

@@ -293,7 +293,12 @@ func (s *Server) handleConfigTheme(w http.ResponseWriter, r *http.Request) {
}, s.cfg.NodeColors, theme.NodeColors)
themeDark := mergeMap(map[string]interface{}{}, s.cfg.ThemeDark, theme.ThemeDark)
typeColors := mergeMap(map[string]interface{}{}, s.cfg.TypeColors, theme.TypeColors)
typeColors := mergeMap(map[string]interface{}{
"ADVERT": "#22c55e", "GRP_TXT": "#3b82f6", "TXT_MSG": "#f59e0b",
"ACK": "#6b7280", "REQUEST": "#a855f7", "RESPONSE": "#06b6d4",
"TRACE": "#ec4899", "PATH": "#14b8a6", "ANON_REQ": "#f43f5e",
"UNKNOWN": "#6b7280",
}, s.cfg.TypeColors, theme.TypeColors)
var home interface{}
if theme.Home != nil {

View File

@@ -454,6 +454,35 @@ func TestConfigThemeEndpoint(t *testing.T) {
}
}
func TestConfigThemeTypeColorsDefaults(t *testing.T) {
_, router := setupTestServer(t)
req := httptest.NewRequest("GET", "/api/config/theme", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != 200 {
t.Fatalf("expected 200, got %d", w.Code)
}
var body map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &body)
tc, ok := body["typeColors"].(map[string]interface{})
if !ok {
t.Fatal("expected typeColors object in theme response")
}
expectedTypes := []string{"ADVERT", "GRP_TXT", "TXT_MSG", "ACK", "REQUEST", "RESPONSE", "TRACE", "PATH", "ANON_REQ", "UNKNOWN"}
for _, typ := range expectedTypes {
val, exists := tc[typ]
if !exists {
t.Errorf("typeColors missing default for %s", typ)
continue
}
color, ok := val.(string)
if !ok || color == "" || color == "#000000" {
t.Errorf("typeColors[%s] should be a non-black color, got %v", typ, val)
}
}
}
func TestConfigMapEndpoint(t *testing.T) {
_, router := setupTestServer(t)
req := httptest.NewRequest("GET", "/api/config/map", nil)

View File

@@ -966,7 +966,7 @@
var stc = server.typeColors || {};
var typeRows = '';
for (var tkey in TYPE_LABELS) {
var tval = tc[tkey] || '#000000';
var tval = tc[tkey] || (window.TYPE_COLORS && window.TYPE_COLORS[tkey]) || '#000000';
typeRows += '<div class="cust-color-row">' +
'<div><label>' + (TYPE_EMOJI[tkey] || '') + ' ' + TYPE_LABELS[tkey] + _overrideDot('typeColors', tkey) + '</label>' +
'<div class="cust-hint">' + (TYPE_HINTS[tkey] || '') + '</div></div>' +

View File

@@ -2020,6 +2020,24 @@ console.log('\n=== customize-v2.js: core behavior ===');
assert.ok(src.includes("surface3: '--surface-3'"), 'surface3 must map to --surface-3');
assert.ok(src.includes("sectionBg: '--section-bg'"), 'sectionBg must map to --section-bg');
});
test('_renderNodes falls back to window.TYPE_COLORS when typeColors is empty (#514)', () => {
const ctx = makeSandbox();
ctx.CustomEvent = function (type) { this.type = type; };
ctx.TYPE_COLORS = { ADVERT: '#22c55e', GRP_TXT: '#3b82f6' };
ctx.window.TYPE_COLORS = ctx.TYPE_COLORS;
const v2 = loadCustomizeV2(ctx);
// computeEffective with empty typeColors should still allow fallback
const server = { typeColors: {} };
const effective = v2.computeEffective(server, {});
// When typeColors is empty, the render should fall back to TYPE_COLORS
// We test the logic directly: tc[key] || TYPE_COLORS[key] || '#000000'
const tc = effective.typeColors || {};
const advertColor = tc['ADVERT'] || (ctx.window.TYPE_COLORS && ctx.window.TYPE_COLORS['ADVERT']) || '#000000';
assert.strictEqual(advertColor, '#22c55e', 'ADVERT should fall back to TYPE_COLORS, not #000000');
const grpColor = tc['GRP_TXT'] || (ctx.window.TYPE_COLORS && ctx.window.TYPE_COLORS['GRP_TXT']) || '#000000';
assert.strictEqual(grpColor, '#3b82f6', 'GRP_TXT should fall back to TYPE_COLORS, not #000000');
});
}
// ===== APP.JS: home rehydration merge (mergeUserHomeConfig removed — dead code) =====