Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions src/datastore/DataStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getMultitextCapacity,
getMultitextDelimiter,
getMultitextJoinDelimiter,
inferMultitextEmptyItem,
} from "@/lib/multitext";
import { DataSourceSchema } from "../charts/schemas/DataSourceSchema";
import { logValidationError } from "@/lib/validationLogging";
Expand Down Expand Up @@ -1494,13 +1495,40 @@ class DataStore {
return !Number.isFinite(v) || (ov.fallbackOnZero && v === 0);
}
//simply return the color associated with the value
if (
c.datatype === "text" ||
c.datatype === "text16" ||
c.datatype === "multitext"
) {
if (c.datatype === "text" || c.datatype === "text16") {
return (x) => colors[data[x]];
}
if (c.datatype === "multitext") {
const capacity = getMultitextCapacity(c);
if (capacity <= 1) {
return (x) => colors[data[x]];
}

const emptyItem = inferMultitextEmptyItem(c);
const emptyIndex = emptyItem == null ? -1 : c.values.indexOf(emptyItem);

return (rowIndex) => {
const start = rowIndex * capacity;
let fallbackColor;

for (let offset = 0; offset < capacity; offset++) {
const valueIndex = data[start + offset];
if (valueIndex == null || valueIndex === 65535) {
break;
Comment on lines +1514 to +1517

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keep scanning past 65535 sentinels inside packed rows.

Line 1516 currently breaks on the first empty slot. That disagrees with Line 503 and src/react/utils/valueReplacementUtil.ts:100-110, which both skip 65535 values anywhere in the packed slice. A row like [0, 65535, 2] would now fall back too early instead of returning the later semantic value.

Possible fix
-                    if (valueIndex == null || valueIndex === 65535) {
-                        break;
+                    if (valueIndex == null) {
+                        break;
+                    }
+                    if (valueIndex === 65535) {
+                        continue;
                     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (let offset = 0; offset < capacity; offset++) {
const valueIndex = data[start + offset];
if (valueIndex == null || valueIndex === 65535) {
break;
for (let offset = 0; offset < capacity; offset++) {
const valueIndex = data[start + offset];
if (valueIndex == null) {
break;
}
if (valueIndex === 65535) {
continue;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/datastore/DataStore.js` around lines 1514 - 1517, The loop in
DataStore.js that iterates "for (let offset = 0; offset < capacity; offset++)"
currently breaks when valueIndex == null || valueIndex === 65535, causing it to
stop scanning packed rows prematurely; change the logic in that loop so it only
treats null/undefined (valueIndex == null) as a break/stop condition but treats
the 65535 sentinel as "skip this slot" and continues scanning (i.e., do not
break on valueIndex === 65535), matching the behavior in valueReplacementUtil.ts
and the handling at line ~503; ensure you still return the first non-sentinel
semantic value found within the packed slice or fall back appropriately if none
exist.

}
if (valueIndex === emptyIndex) {
fallbackColor = colors[valueIndex];
continue;
}
// Packed multitext rows can contain multiple values.
// Until charts support multi-value categorical coloring directly,
// use the first semantic row item deterministically.
return colors[valueIndex];
}

return fallbackColor;
};
}
if (
c.datatype === "integer" ||
c.datatype === "double" ||
Expand Down
62 changes: 62 additions & 0 deletions src/tests/datastore-color.multitext.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import DataStore from "@/datastore/DataStore";
import { describe, expect, test } from "vitest";

describe("DataStore.getColorFunction", () => {
test("keeps legacy single-slot multitext coloring", () => {
const column = {
field: "__tags__",
name: "tags",
datatype: "multitext" as const,
delimiter: ";",
stringLength: 1,
values: ["a", "b", "a; b"],
data: new Uint16Array([0, 1, 2]),
};

const store = {
columnIndex: { __tags__: column },
getColumnColors: () => ["#ff0000", "#00ff00", "#0000ff"],
};

const colorFn = DataStore.prototype.getColorFunction.call(
store,
"__tags__",
{},
);

expect(colorFn(0)).toBe("#ff0000");
expect(colorFn(1)).toBe("#00ff00");
expect(colorFn(2)).toBe("#0000ff");
});

test("reads packed multitext colors using row stride", () => {
const column = {
field: "__gates__",
name: "gates",
datatype: "multitext" as const,
delimiter: ",",
stringLength: 3,
values: ["N/A", "a", "b"],
data: new Uint16Array([
0, 65535, 65535,
2, 65535, 65535,
1, 2, 65535,
]),
};

const store = {
columnIndex: { __gates__: column },
getColumnColors: () => ["#999999", "#ff0000", "#00ff00"],
};

const colorFn = DataStore.prototype.getColorFunction.call(
store,
"__gates__",
{},
);

expect(colorFn(0)).toBe("#999999");
expect(colorFn(1)).toBe("#00ff00");
expect(colorFn(2)).toBe("#ff0000");
});
});
Loading