Skip to content

Commit c186bf4

Browse files
authored
Merge pull request #145 from LennartvdM/codex/fix-radialurchin-wiring-and-visuals
Fix radial visuals payload handling
2 parents 3a884c9 + f7ba32e commit c186bf4

2 files changed

Lines changed: 82 additions & 20 deletions

File tree

web/app.js

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ const visualsState = {
9999
runLabel: null,
100100
};
101101
let lastVisualPayload = null;
102+
let lastVisualSchedule = null;
102103
let isGeneratingCalendar = false;
103104
const GENERATE_BUTTON_DEFAULT_LABEL = 'Generate schedule';
104105
const GENERATE_BUTTON_LOADING_LABEL = 'Generating…';
@@ -242,8 +243,36 @@ function resolveLegacyPng(payload) {
242243
return candidates.find((value) => typeof value === 'string' && value.trim().length > 0) || '';
243244
}
244245

246+
function resolveVisualPayload(payload) {
247+
if (!payload || typeof payload !== 'object') {
248+
return null;
249+
}
250+
if (Array.isArray(payload.events)) {
251+
return payload;
252+
}
253+
if (payload.calendar && typeof payload.calendar === 'object') {
254+
return resolveVisualPayload(payload.calendar);
255+
}
256+
if (payload.calendarJson && typeof payload.calendarJson === 'object') {
257+
return resolveVisualPayload(payload.calendarJson);
258+
}
259+
if (payload.rawResult && typeof payload.rawResult === 'object') {
260+
return resolveVisualPayload(payload.rawResult);
261+
}
262+
if (payload.data && typeof payload.data === 'object') {
263+
return resolveVisualPayload(payload.data);
264+
}
265+
return null;
266+
}
267+
245268
function updateVisuals(payload) {
246269
lastVisualPayload = payload && typeof payload === 'object' ? payload : null;
270+
lastVisualSchedule = resolveVisualPayload(payload);
271+
272+
if (!lastVisualSchedule && payload && typeof payload === 'object' && !visualsState.useLegacy) {
273+
console.warn('[visuals] received payload without events, skipping radial render');
274+
}
275+
247276
if (visualsState.useLegacy) {
248277
resetVisualsInstance();
249278
const src = resolveLegacyPng(lastVisualPayload);
@@ -267,12 +296,12 @@ function updateVisuals(payload) {
267296
}
268297
}
269298
} else {
270-
maybeCreateUrchinInstance(lastVisualPayload);
299+
maybeCreateUrchinInstance(lastVisualSchedule);
271300
}
272301

273302
if (visualsState.urchin) {
274303
try {
275-
visualsState.urchin.update({ data: visualsState.useLegacy ? null : lastVisualPayload });
304+
visualsState.urchin.update({ data: visualsState.useLegacy ? null : lastVisualSchedule });
276305
} catch (error) {
277306
console.error('[visuals] failed to update radial urchin:', error);
278307
}
@@ -298,21 +327,23 @@ function resetVisualsInstance() {
298327
}
299328

300329
function hasVisualEvents(payload) {
301-
return (
302-
payload &&
303-
typeof payload === 'object' &&
304-
Array.isArray(payload.events) &&
305-
payload.events.length > 0
306-
);
330+
const schedule = resolveVisualPayload(payload);
331+
return Boolean(schedule && Array.isArray(schedule.events) && schedule.events.length > 0);
307332
}
308333

309-
function maybeCreateUrchinInstance(payload) {
310-
if (visualsState.useLegacy || visualsState.urchin || !visualsState.mount || !hasVisualEvents(payload)) {
334+
function maybeCreateUrchinInstance(schedule) {
335+
if (
336+
visualsState.useLegacy ||
337+
visualsState.urchin ||
338+
!visualsState.mount ||
339+
!schedule ||
340+
!hasVisualEvents(schedule)
341+
) {
311342
return;
312343
}
313344
resetVisualsInstance();
314345
const instance = createRadialUrchin(visualsState.mount, {
315-
data: payload,
346+
data: schedule,
316347
mode: 'day-rings',
317348
onSelect: handleUrchinSelect,
318349
});
@@ -500,7 +531,9 @@ function ensureCalendarHistorySummary(entry) {
500531
if (entry.summary && typeof entry.summary === 'object') {
501532
return entry.summary;
502533
}
503-
const events = entry.rawResult && Array.isArray(entry.rawResult.events)
534+
const events = entry.calendarJson && Array.isArray(entry.calendarJson.events)
535+
? entry.calendarJson.events
536+
: entry.rawResult && Array.isArray(entry.rawResult.events)
504537
? entry.rawResult.events
505538
: null;
506539
if (!events) {
@@ -756,9 +789,15 @@ function renderCalendarHistorySummary() {
756789
function setCurrentCalendarHistoryEntry(entry, options = {}) {
757790
const { updateJson = true, focusVisuals = false, showEmptyState = true } = options;
758791

759-
const payload = entry && entry.rawResult ? cloneCalendarHistoryPayload(entry.rawResult) || entry.rawResult : null;
792+
const rawPayload =
793+
entry && entry.rawResult ? cloneCalendarHistoryPayload(entry.rawResult) || entry.rawResult : null;
794+
const calendarPayloadSource =
795+
entry && entry.calendarJson
796+
? cloneCalendarHistoryPayload(entry.calendarJson) || entry.calendarJson
797+
: rawPayload;
798+
const visualPayload = resolveVisualPayload(calendarPayloadSource);
760799

761-
if (!entry || !payload || typeof payload !== 'object') {
800+
if (!entry || !visualPayload || typeof visualPayload !== 'object') {
762801
calendarHistoryState.activeId = null;
763802
calendarHistoryState.currentRun = null;
764803
updateVisuals(null);
@@ -770,19 +809,20 @@ function setCurrentCalendarHistoryEntry(entry, options = {}) {
770809
calendarHistoryState.currentRun = {
771810
...entry,
772811
summary: entry.summary && typeof entry.summary === 'object' ? { ...entry.summary } : null,
773-
rawResult: cloneCalendarHistoryPayload(payload) || payload,
812+
rawResult: rawPayload || visualPayload,
813+
calendarJson: cloneCalendarHistoryPayload(visualPayload) || visualPayload,
774814
};
775815

776816
if (updateJson) {
777-
setJsonPayload(payload, {
817+
setJsonPayload(rawPayload || visualPayload, {
778818
variant: entry.variant,
779819
rig: entry.rig,
780820
weekStart: entry.weekStart,
781821
});
782-
const validation = validateWebV1Calendar(payload);
822+
const validation = validateWebV1Calendar(rawPayload || visualPayload || {});
783823
setJsonValidationBadge(validation.ok ? 'ok' : 'err');
784824
} else {
785-
updateVisuals(payload);
825+
updateVisuals(rawPayload || visualPayload);
786826
}
787827

788828
hideVisualsOverlay();
@@ -865,6 +905,7 @@ function recordCalendarHistoryEntry(entry) {
865905
if (!entry || !entry.rawResult) {
866906
return;
867907
}
908+
const visualPayload = resolveVisualPayload(entry.calendarJson || entry.rawResult);
868909
const normalized = {
869910
id: entry.id || generateCalendarHistoryId(),
870911
timestamp: entry.timestamp || new Date().toISOString(),
@@ -880,6 +921,7 @@ function recordCalendarHistoryEntry(entry) {
880921
weekStart: entry.weekStart || '',
881922
summary: entry.summary ? { ...entry.summary } : null,
882923
rawResult: cloneCalendarHistoryPayload(entry.rawResult) || entry.rawResult,
924+
calendarJson: cloneCalendarHistoryPayload(visualPayload) || visualPayload,
883925
};
884926

885927
calendarHistoryState.runHistory = [normalized, ...calendarHistoryState.runHistory].slice(
@@ -1247,8 +1289,9 @@ function setJsonPayload(payload, options = {}) {
12471289
: snapshot.week_start || '';
12481290
metadata.week = weekFromOptions;
12491291

1250-
if (parsedPayload && Array.isArray(parsedPayload.events)) {
1251-
metadata.events = parsedPayload.events.length;
1292+
const scheduleForMeta = resolveVisualPayload(parsedPayload);
1293+
if (scheduleForMeta && Array.isArray(scheduleForMeta.events)) {
1294+
metadata.events = scheduleForMeta.events.length;
12521295
}
12531296

12541297
currentJsonMetadata = metadata;

web/ui/visuals/RadialUrchin.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export class RadialUrchin {
138138
this.contrastQuery = null;
139139
this.hasRenderableData = false;
140140
this.didWarnNoData = false;
141+
this.didWarnInvalidCenter = false;
141142

142143
this.handleResize = this.handleResize.bind(this);
143144
this.handlePointerMove = this.handlePointerMove.bind(this);
@@ -646,6 +647,7 @@ export class RadialUrchin {
646647
this.overlay.setAttribute('width', width);
647648
this.overlay.setAttribute('height', height);
648649
this.center = { x: width / 2, y: height / 2 };
650+
this.didWarnInvalidCenter = false;
649651
this.canvasRect = getElementRect(this.canvas);
650652
this.rebuildDisplayArcs();
651653
this.render();
@@ -661,6 +663,23 @@ export class RadialUrchin {
661663
return;
662664
}
663665

666+
if (
667+
!this.center ||
668+
typeof this.center.x !== 'number' ||
669+
Number.isNaN(this.center.x) ||
670+
typeof this.center.y !== 'number' ||
671+
Number.isNaN(this.center.y)
672+
) {
673+
if (!this.didWarnInvalidCenter) {
674+
console.warn('[RadialUrchin] invalid center point, skipping render', this.center);
675+
this.didWarnInvalidCenter = true;
676+
}
677+
this.updateSelectionOverlay();
678+
this.updateScrubOverlay();
679+
return;
680+
}
681+
this.didWarnInvalidCenter = false;
682+
664683
if (!this.hasRenderableData || !this.layout || !Array.isArray(this.layout.arcs) || this.layout.arcs.length === 0) {
665684
ctx.clearRect(0, 0, this.offscreen.width, this.offscreen.height);
666685
mainCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);

0 commit comments

Comments
 (0)