epiq 1.4.2 → 1.4.3
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/gui/main.js +971 -188
- package/dist/index.js +49 -49
- package/dist/mcp.js +37 -37
- package/package.json +1 -1
package/dist/gui/main.js
CHANGED
|
@@ -24860,6 +24860,12 @@ var GUI_THEME = {
|
|
|
24860
24860
|
green: "#8ce99a",
|
|
24861
24861
|
red: "#ff8787"
|
|
24862
24862
|
};
|
|
24863
|
+
var EVENT_CATEGORY_COLORS = {
|
|
24864
|
+
tickets: GUI_THEME.accent,
|
|
24865
|
+
comments: "#ffd479",
|
|
24866
|
+
tagging: "#c9a5ff",
|
|
24867
|
+
assigning: "#ff9ecd"
|
|
24868
|
+
};
|
|
24863
24869
|
|
|
24864
24870
|
// source/gui/client/components/Aside.tsx
|
|
24865
24871
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
@@ -25318,7 +25324,7 @@ var User = ({
|
|
|
25318
25324
|
);
|
|
25319
25325
|
|
|
25320
25326
|
// source/version.ts
|
|
25321
|
-
var EPIQ_VERSION = "1.4.
|
|
25327
|
+
var EPIQ_VERSION = "1.4.3";
|
|
25322
25328
|
|
|
25323
25329
|
// source/gui/client/components/Header.tsx
|
|
25324
25330
|
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
@@ -39897,7 +39903,15 @@ var TicketCard = ({
|
|
|
39897
39903
|
paddingTop: 2,
|
|
39898
39904
|
marginTop: "-4px"
|
|
39899
39905
|
},
|
|
39900
|
-
children: ticket.assignees.map((assignee, idx) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
39906
|
+
children: ticket.assignees.map((assignee, idx) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
39907
|
+
User,
|
|
39908
|
+
{
|
|
39909
|
+
user: assignee,
|
|
39910
|
+
index: idx,
|
|
39911
|
+
isFocus: isSelected
|
|
39912
|
+
},
|
|
39913
|
+
assignee.id
|
|
39914
|
+
))
|
|
39901
39915
|
}
|
|
39902
39916
|
)
|
|
39903
39917
|
]
|
|
@@ -40203,7 +40217,7 @@ var EVENTS_MODE_TOTAL_HEIGHT = 8 + TRACK_HEIGHT * 2;
|
|
|
40203
40217
|
var EVENTS_SCATTER_HEIGHT = TRACK_HEIGHT + 16;
|
|
40204
40218
|
var EVENTS_MODE_VERTICAL_PADDING = (EVENTS_MODE_TOTAL_HEIGHT - EVENTS_SCATTER_HEIGHT) / 2;
|
|
40205
40219
|
var HOVER_HINT_WIDTH = 220;
|
|
40206
|
-
var SEGMENT_HIGHLIGHT_COLOR = "rgba(122,
|
|
40220
|
+
var SEGMENT_HIGHLIGHT_COLOR = "rgba(122, 157, 214, 0.14)";
|
|
40207
40221
|
var BUCKET_HIGHLIGHT_COLOR = "rgba(255, 255, 255, 0.06)";
|
|
40208
40222
|
var NEEDLE_COLOR = "rgba(255, 255, 255, 0.62)";
|
|
40209
40223
|
var MIN_BUCKET_COUNT_FOR_GAP = 300;
|
|
@@ -40239,8 +40253,15 @@ var buildAxis = (timeline, commits, now = Date.now()) => {
|
|
|
40239
40253
|
fractionToTime: (fraction) => Math.round(earliest + clamp(fraction, 0, 1) * span)
|
|
40240
40254
|
};
|
|
40241
40255
|
};
|
|
40242
|
-
var bucketIssueCounts = (axis, timeline) => {
|
|
40256
|
+
var bucketIssueCounts = (axis, timeline, view = "all", hiddenIds = /* @__PURE__ */ new Set()) => {
|
|
40243
40257
|
const counts = new Array(axis.bucketCount).fill(0);
|
|
40258
|
+
if (timeline && timeline.events.length > 0) {
|
|
40259
|
+
for (const entry of timeline.events) {
|
|
40260
|
+
if (!isShown(entry, view, hiddenIds)) continue;
|
|
40261
|
+
counts[axis.bucketIndexForTime(entry.t)] += 1;
|
|
40262
|
+
}
|
|
40263
|
+
return counts;
|
|
40264
|
+
}
|
|
40244
40265
|
for (const sparseBucket of timeline?.buckets ?? []) {
|
|
40245
40266
|
counts[axis.bucketIndexForTime(sparseBucket.t)] += sparseBucket.count;
|
|
40246
40267
|
}
|
|
@@ -40258,6 +40279,102 @@ var bucketCommitStats = (axis, commits) => {
|
|
|
40258
40279
|
}
|
|
40259
40280
|
return byIndex;
|
|
40260
40281
|
};
|
|
40282
|
+
var EVENT_CATEGORIES = [
|
|
40283
|
+
"tickets",
|
|
40284
|
+
"comments",
|
|
40285
|
+
"tagging",
|
|
40286
|
+
"assigning"
|
|
40287
|
+
];
|
|
40288
|
+
var BOARD_VIEWS = ["all", ...EVENT_CATEGORIES];
|
|
40289
|
+
var isBoardView = (value) => BOARD_VIEWS.includes(value);
|
|
40290
|
+
var boardViewColor = (view) => view === "all" ? GUI_THEME.accent : EVENT_CATEGORY_COLORS[view];
|
|
40291
|
+
var identityAxisFor = (view) => view === "comments" ? "actor" : view === "tagging" ? "tag" : view === "assigning" ? "assignee" : null;
|
|
40292
|
+
var CATEGORY_BY_ACTION = {
|
|
40293
|
+
"add.issue.comment": "comments",
|
|
40294
|
+
"edit.issue.comment": "comments",
|
|
40295
|
+
"delete.issue.comment": "comments",
|
|
40296
|
+
"add.issue.tag": "tagging",
|
|
40297
|
+
"remove.issue.tag": "tagging",
|
|
40298
|
+
"create.tag": "tagging",
|
|
40299
|
+
"add.issue.assignee": "assigning",
|
|
40300
|
+
"remove.issue.assignee": "assigning",
|
|
40301
|
+
"create.contributor": "assigning",
|
|
40302
|
+
"tombstone.contributor": "assigning",
|
|
40303
|
+
"restore.contributor": "assigning",
|
|
40304
|
+
"link.contributor.user": "assigning"
|
|
40305
|
+
};
|
|
40306
|
+
var categoryOf = (action) => CATEGORY_BY_ACTION[action] ?? "tickets";
|
|
40307
|
+
var identityOf = (entry, view) => {
|
|
40308
|
+
const axis = identityAxisFor(view);
|
|
40309
|
+
return axis === null ? null : entry[axis];
|
|
40310
|
+
};
|
|
40311
|
+
var listIdentities = (timeline, view) => {
|
|
40312
|
+
if (!timeline || identityAxisFor(view) === null) return [];
|
|
40313
|
+
const byId = /* @__PURE__ */ new Map();
|
|
40314
|
+
for (const entry of timeline.events) {
|
|
40315
|
+
if (categoryOf(entry.action) !== view) continue;
|
|
40316
|
+
const identity = identityOf(entry, view);
|
|
40317
|
+
if (identity && !byId.has(identity.id)) byId.set(identity.id, identity);
|
|
40318
|
+
}
|
|
40319
|
+
return [...byId.values()];
|
|
40320
|
+
};
|
|
40321
|
+
var soleVisibleIdentity = (identities, hiddenIds) => {
|
|
40322
|
+
const visible = identities.filter((identity) => !hiddenIds.has(identity.id));
|
|
40323
|
+
return visible.length === 1 ? visible[0] : null;
|
|
40324
|
+
};
|
|
40325
|
+
var dotDetail = (dot) => {
|
|
40326
|
+
const base = dot.label ?? `${dot.count ?? 0} board event${dot.count === 1 ? "" : "s"}`;
|
|
40327
|
+
return dot.identity && !base.includes(dot.identity.name) ? `${base} \u2014 ${dot.identity.name}` : base;
|
|
40328
|
+
};
|
|
40329
|
+
var EVENT_DOT_SIZE = 4;
|
|
40330
|
+
var EVENT_DOT_OPACITY = 0.55;
|
|
40331
|
+
var isShown = (entry, view, hiddenIds) => {
|
|
40332
|
+
if (view !== "all" && categoryOf(entry.action) !== view) return false;
|
|
40333
|
+
const identity = identityOf(entry, view);
|
|
40334
|
+
return identity === null || !hiddenIds.has(identity.id);
|
|
40335
|
+
};
|
|
40336
|
+
var buildEventDots = (timeline, view = "all", hiddenIds = /* @__PURE__ */ new Set()) => {
|
|
40337
|
+
if (!timeline) return [];
|
|
40338
|
+
if (timeline.events.length > 0) {
|
|
40339
|
+
return timeline.events.flatMap((entry, index2) => {
|
|
40340
|
+
if (!isShown(entry, view, hiddenIds)) return [];
|
|
40341
|
+
const category = categoryOf(entry.action);
|
|
40342
|
+
const identity = identityOf(entry, view);
|
|
40343
|
+
return [
|
|
40344
|
+
{
|
|
40345
|
+
// Two events can share a millisecond, so time alone is not a key.
|
|
40346
|
+
key: `${entry.t}-${index2}`,
|
|
40347
|
+
t: entry.t,
|
|
40348
|
+
count: null,
|
|
40349
|
+
label: entry.label,
|
|
40350
|
+
category,
|
|
40351
|
+
color: view === "all" ? EVENT_CATEGORY_COLORS[category] : identity?.color ?? GUI_THEME.accent,
|
|
40352
|
+
identity,
|
|
40353
|
+
size: EVENT_DOT_SIZE,
|
|
40354
|
+
opacity: EVENT_DOT_OPACITY
|
|
40355
|
+
}
|
|
40356
|
+
];
|
|
40357
|
+
});
|
|
40358
|
+
}
|
|
40359
|
+
const maxCount = maxOf(
|
|
40360
|
+
timeline.buckets.map((bucket) => bucket.count),
|
|
40361
|
+
1
|
|
40362
|
+
);
|
|
40363
|
+
return timeline.buckets.map((bucket) => {
|
|
40364
|
+
const intensity = bucket.count / maxCount;
|
|
40365
|
+
return {
|
|
40366
|
+
key: String(bucket.t),
|
|
40367
|
+
t: bucket.t,
|
|
40368
|
+
count: bucket.count,
|
|
40369
|
+
label: null,
|
|
40370
|
+
category: null,
|
|
40371
|
+
color: GUI_THEME.accent,
|
|
40372
|
+
identity: null,
|
|
40373
|
+
size: 3 + intensity * 6,
|
|
40374
|
+
opacity: 0.3 + intensity * 0.5
|
|
40375
|
+
};
|
|
40376
|
+
});
|
|
40377
|
+
};
|
|
40261
40378
|
var populatedRange = (bars) => bars.length > 0 ? [
|
|
40262
40379
|
bars.reduce((min, bar) => Math.min(min, bar.index), Infinity),
|
|
40263
40380
|
bars.reduce((max, bar) => Math.max(max, bar.index), -Infinity)
|
|
@@ -40266,8 +40383,9 @@ var hourFractionForTime = (time) => {
|
|
|
40266
40383
|
const date = new Date(time);
|
|
40267
40384
|
return (date.getHours() * 60 + date.getMinutes()) / (24 * 60);
|
|
40268
40385
|
};
|
|
40269
|
-
var SEGMENT_UNIT_ORDER = ["day", "week", "month", "year"];
|
|
40386
|
+
var SEGMENT_UNIT_ORDER = ["hour", "day", "week", "month", "year"];
|
|
40270
40387
|
var APPROX_UNIT_MS = {
|
|
40388
|
+
hour: 60 * 60 * 1e3,
|
|
40271
40389
|
day: 24 * 60 * 60 * 1e3,
|
|
40272
40390
|
week: 7 * 24 * 60 * 60 * 1e3,
|
|
40273
40391
|
month: 30.44 * 24 * 60 * 60 * 1e3,
|
|
@@ -40293,14 +40411,16 @@ var MONTH_LABELS = [
|
|
|
40293
40411
|
"Dec"
|
|
40294
40412
|
];
|
|
40295
40413
|
var advanceByUnit = (date, unit) => {
|
|
40296
|
-
if (unit === "
|
|
40414
|
+
if (unit === "hour") date.setHours(date.getHours() + 1);
|
|
40415
|
+
else if (unit === "day") date.setDate(date.getDate() + 1);
|
|
40297
40416
|
else if (unit === "week") date.setDate(date.getDate() + 7);
|
|
40298
40417
|
else if (unit === "month") date.setMonth(date.getMonth() + 1);
|
|
40299
40418
|
else date.setFullYear(date.getFullYear() + 1);
|
|
40300
40419
|
};
|
|
40301
40420
|
var segmentAt = (time, unit) => {
|
|
40302
40421
|
const start2 = new Date(time);
|
|
40303
|
-
start2.
|
|
40422
|
+
if (unit === "hour") start2.setMinutes(0, 0, 0);
|
|
40423
|
+
else start2.setHours(0, 0, 0, 0);
|
|
40304
40424
|
if (unit === "week") {
|
|
40305
40425
|
start2.setDate(start2.getDate() - (start2.getDay() + 6) % 7);
|
|
40306
40426
|
} else if (unit === "month") {
|
|
@@ -40311,7 +40431,10 @@ var segmentAt = (time, unit) => {
|
|
|
40311
40431
|
const end = new Date(start2);
|
|
40312
40432
|
advanceByUnit(end, unit);
|
|
40313
40433
|
const lastDay = new Date(end.getTime() - 1);
|
|
40314
|
-
const label = unit === "
|
|
40434
|
+
const label = unit === "hour" ? `${WEEKDAY_LABELS[start2.getDay()]} ${String(start2.getHours()).padStart(
|
|
40435
|
+
2,
|
|
40436
|
+
"0"
|
|
40437
|
+
)}:00` : unit === "day" ? `${WEEKDAY_LABELS[start2.getDay()]} ${start2.getDate()} ${MONTH_LABELS[start2.getMonth()]}` : unit === "week" ? start2.getMonth() === lastDay.getMonth() ? `${start2.getDate()}\u2013${lastDay.getDate()} ${MONTH_LABELS[start2.getMonth()]}` : `${start2.getDate()} ${MONTH_LABELS[start2.getMonth()]} \u2013 ${lastDay.getDate()} ${MONTH_LABELS[lastDay.getMonth()]}` : unit === "month" ? `${MONTH_LABELS[start2.getMonth()]} ${start2.getFullYear()}` : `${start2.getFullYear()}`;
|
|
40315
40438
|
return { start: start2.getTime(), end: end.getTime(), label };
|
|
40316
40439
|
};
|
|
40317
40440
|
var formatInterval = (start2, end) => {
|
|
@@ -40320,12 +40443,19 @@ var formatInterval = (start2, end) => {
|
|
|
40320
40443
|
const endLabel = isSameDay(startDate, endDate) ? formatTimeOfDay(endDate) : formatDateTime(endDate);
|
|
40321
40444
|
return `${formatDateTime(startDate)} \u2013 ${endLabel}`;
|
|
40322
40445
|
};
|
|
40323
|
-
var SCOPES = ["week", "month", "year", "all"];
|
|
40446
|
+
var SCOPES = ["day", "week", "month", "year", "all"];
|
|
40324
40447
|
var SCOPE_DURATION_MS = {
|
|
40448
|
+
day: 24 * 60 * 60 * 1e3,
|
|
40325
40449
|
week: 7 * 24 * 60 * 60 * 1e3,
|
|
40326
40450
|
month: 30 * 24 * 60 * 60 * 1e3,
|
|
40327
40451
|
year: 365 * 24 * 60 * 60 * 1e3
|
|
40328
40452
|
};
|
|
40453
|
+
var SCOPE_RECENT_LABELS = {
|
|
40454
|
+
day: "Last 24 hours",
|
|
40455
|
+
week: "Last 7 days",
|
|
40456
|
+
month: "Last 30 days",
|
|
40457
|
+
year: "Last 365 days"
|
|
40458
|
+
};
|
|
40329
40459
|
var isScope = (value) => SCOPES.includes(value ?? "");
|
|
40330
40460
|
var getPeriodRange = (scope, offset) => {
|
|
40331
40461
|
if (scope === "all") return null;
|
|
@@ -40335,9 +40465,7 @@ var getPeriodRange = (scope, offset) => {
|
|
|
40335
40465
|
};
|
|
40336
40466
|
var formatPeriodLabel = (scope, offset, range) => {
|
|
40337
40467
|
if (scope === "all" || !range) return "All time";
|
|
40338
|
-
if (offset === 0)
|
|
40339
|
-
return scope === "week" ? "Last 7 days" : scope === "month" ? "Last 30 days" : "Last 365 days";
|
|
40340
|
-
}
|
|
40468
|
+
if (offset === 0) return SCOPE_RECENT_LABELS[scope];
|
|
40341
40469
|
const start2 = new Date(range.start);
|
|
40342
40470
|
const end = new Date(range.end);
|
|
40343
40471
|
return `${start2.getMonth() + 1}/${start2.getDate()} \u2013 ${end.getMonth() + 1}/${end.getDate()}`;
|
|
@@ -40354,10 +40482,16 @@ var hashUnitInterval = (key) => {
|
|
|
40354
40482
|
var DOT_APPEAR_MS = 260;
|
|
40355
40483
|
var DOT_APPEAR_SCATTER_MS = 620;
|
|
40356
40484
|
var dotDelayMs = (key) => Math.round(hashUnitInterval(key) * DOT_APPEAR_SCATTER_MS);
|
|
40357
|
-
var
|
|
40358
|
-
|
|
40359
|
-
)
|
|
40360
|
-
var
|
|
40485
|
+
var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
40486
|
+
var easeInCubic = (t) => t * t * t;
|
|
40487
|
+
var dotEntranceScale = (key, elapsedMs) => easeOutCubic(clamp((elapsedMs - dotDelayMs(key)) / DOT_APPEAR_MS, 0, 1));
|
|
40488
|
+
var dotExitScale = (key, elapsedMs) => 1 - easeInCubic(
|
|
40489
|
+
clamp(
|
|
40490
|
+
(elapsedMs - (DOT_APPEAR_SCATTER_MS - dotDelayMs(key))) / DOT_APPEAR_MS,
|
|
40491
|
+
0,
|
|
40492
|
+
1
|
|
40493
|
+
)
|
|
40494
|
+
);
|
|
40361
40495
|
var DOT_EXIT_TOTAL_MS = DOT_APPEAR_SCATTER_MS + DOT_APPEAR_MS;
|
|
40362
40496
|
var BAR_GROW_MS = 200;
|
|
40363
40497
|
var BAR_GROW_SWEEP_MS = 560;
|
|
@@ -40450,6 +40584,19 @@ var usePersistedFlag = (key, fallback) => {
|
|
|
40450
40584
|
}
|
|
40451
40585
|
];
|
|
40452
40586
|
};
|
|
40587
|
+
var buildBoardFilter = (view, identities, hiddenIds) => {
|
|
40588
|
+
const axis = identityAxisFor(view);
|
|
40589
|
+
if (axis === null || hiddenIds.size === 0) return null;
|
|
40590
|
+
const visibleIds = new Set(
|
|
40591
|
+
identities.map((identity) => identity.id).filter((id) => !hiddenIds.has(id))
|
|
40592
|
+
);
|
|
40593
|
+
return { axis, visibleIds };
|
|
40594
|
+
};
|
|
40595
|
+
var issuePassesBoardFilter = (issue, commentAuthorIds, filter) => {
|
|
40596
|
+
if (!filter) return true;
|
|
40597
|
+
const ids = filter.axis === "tag" ? issue.tags.map((tag) => tag.id) : filter.axis === "assignee" ? issue.assignees.map((assignee) => assignee.id) : commentAuthorIds;
|
|
40598
|
+
return ids.some((id) => filter.visibleIds.has(id));
|
|
40599
|
+
};
|
|
40453
40600
|
|
|
40454
40601
|
// source/gui/client/components/ScrubberParts.tsx
|
|
40455
40602
|
var import_react14 = __toESM(require_react(), 1);
|
|
@@ -40581,6 +40728,319 @@ var toggleButtonStyle = (active) => ({
|
|
|
40581
40728
|
padding: "2px 8px",
|
|
40582
40729
|
cursor: "pointer"
|
|
40583
40730
|
});
|
|
40731
|
+
var CATEGORY_LABELS = {
|
|
40732
|
+
tickets: "Tickets",
|
|
40733
|
+
comments: "Comments",
|
|
40734
|
+
tagging: "Tags",
|
|
40735
|
+
assigning: "Assignees"
|
|
40736
|
+
};
|
|
40737
|
+
var disclosureStyle = {
|
|
40738
|
+
background: "transparent",
|
|
40739
|
+
border: "none",
|
|
40740
|
+
padding: 0,
|
|
40741
|
+
display: "inline-flex",
|
|
40742
|
+
alignItems: "center",
|
|
40743
|
+
color: GUI_THEME.secondary,
|
|
40744
|
+
cursor: "pointer"
|
|
40745
|
+
};
|
|
40746
|
+
var VIEW_LABELS = {
|
|
40747
|
+
all: "All board events",
|
|
40748
|
+
...CATEGORY_LABELS
|
|
40749
|
+
};
|
|
40750
|
+
var SELECT_TRIGGER_WIDTH = 180;
|
|
40751
|
+
var selectTriggerStyle = (color2, disabled) => ({
|
|
40752
|
+
display: "inline-flex",
|
|
40753
|
+
alignItems: "center",
|
|
40754
|
+
justifyContent: "space-between",
|
|
40755
|
+
gap: 6,
|
|
40756
|
+
width: SELECT_TRIGGER_WIDTH,
|
|
40757
|
+
boxSizing: "border-box",
|
|
40758
|
+
background: "rgba(208, 223, 255, 0.08)",
|
|
40759
|
+
// None, but padded as though there were, so it sits at the same height as
|
|
40760
|
+
// the bordered toggles on either side.
|
|
40761
|
+
border: "none",
|
|
40762
|
+
color: disabled ? GUI_THEME.dim : color2,
|
|
40763
|
+
borderRadius: 6,
|
|
40764
|
+
fontFamily: "inherit",
|
|
40765
|
+
fontSize: 10,
|
|
40766
|
+
padding: "3px 7px 3px 9px",
|
|
40767
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
40768
|
+
opacity: disabled ? 0.4 : 1
|
|
40769
|
+
});
|
|
40770
|
+
var nestedListStyle = {
|
|
40771
|
+
display: "flex",
|
|
40772
|
+
flexDirection: "column",
|
|
40773
|
+
gap: 7,
|
|
40774
|
+
marginLeft: 5,
|
|
40775
|
+
paddingLeft: 8,
|
|
40776
|
+
borderLeft: `1px solid ${GUI_THEME.line}`
|
|
40777
|
+
};
|
|
40778
|
+
var popoverStyle = {
|
|
40779
|
+
position: "absolute",
|
|
40780
|
+
top: "100%",
|
|
40781
|
+
left: 0,
|
|
40782
|
+
marginTop: 6,
|
|
40783
|
+
// Carries the column and its gap itself. Without them the options stack as
|
|
40784
|
+
// plain blocks and their radios sit edge to edge.
|
|
40785
|
+
display: "flex",
|
|
40786
|
+
flexDirection: "column",
|
|
40787
|
+
gap: 7,
|
|
40788
|
+
padding: "10px 14px 10px 10px",
|
|
40789
|
+
// Sized for the common case up front, so opening a kind with a list does
|
|
40790
|
+
// not visibly widen the panel under the pointer.
|
|
40791
|
+
minWidth: 200,
|
|
40792
|
+
background: GUI_THEME.panel2,
|
|
40793
|
+
border: `1px solid ${GUI_THEME.line}`,
|
|
40794
|
+
borderRadius: 8,
|
|
40795
|
+
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.45)",
|
|
40796
|
+
// Above the dots and the needle, which sit at 1 and 2.
|
|
40797
|
+
zIndex: 30,
|
|
40798
|
+
whiteSpace: "nowrap"
|
|
40799
|
+
};
|
|
40800
|
+
var onlyButtonStyle = {
|
|
40801
|
+
background: "transparent",
|
|
40802
|
+
border: `1px solid ${GUI_THEME.line}`,
|
|
40803
|
+
borderRadius: 4,
|
|
40804
|
+
color: GUI_THEME.dim,
|
|
40805
|
+
fontFamily: "inherit",
|
|
40806
|
+
fontSize: 9,
|
|
40807
|
+
lineHeight: 1,
|
|
40808
|
+
padding: "2px 4px",
|
|
40809
|
+
cursor: "pointer"
|
|
40810
|
+
};
|
|
40811
|
+
var useDismissOnOutsideClick = (open, onDismiss) => {
|
|
40812
|
+
const ref = (0, import_react14.useRef)(null);
|
|
40813
|
+
(0, import_react14.useEffect)(() => {
|
|
40814
|
+
if (!open) return;
|
|
40815
|
+
const onPointerDown = (event) => {
|
|
40816
|
+
if (!ref.current?.contains(event.target)) onDismiss();
|
|
40817
|
+
};
|
|
40818
|
+
const onKeyDown = (event) => {
|
|
40819
|
+
if (event.key === "Escape") onDismiss();
|
|
40820
|
+
};
|
|
40821
|
+
document.addEventListener("mousedown", onPointerDown);
|
|
40822
|
+
document.addEventListener("keydown", onKeyDown);
|
|
40823
|
+
return () => {
|
|
40824
|
+
document.removeEventListener("mousedown", onPointerDown);
|
|
40825
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
40826
|
+
};
|
|
40827
|
+
}, [open, onDismiss]);
|
|
40828
|
+
return ref;
|
|
40829
|
+
};
|
|
40830
|
+
var Radio = ({
|
|
40831
|
+
label,
|
|
40832
|
+
selected,
|
|
40833
|
+
color: color2,
|
|
40834
|
+
disabled,
|
|
40835
|
+
onSelect
|
|
40836
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
40837
|
+
"button",
|
|
40838
|
+
{
|
|
40839
|
+
type: "button",
|
|
40840
|
+
role: "radio",
|
|
40841
|
+
"aria-checked": selected,
|
|
40842
|
+
disabled,
|
|
40843
|
+
onClick: onSelect,
|
|
40844
|
+
style: {
|
|
40845
|
+
display: "flex",
|
|
40846
|
+
alignItems: "center",
|
|
40847
|
+
gap: 5,
|
|
40848
|
+
background: "transparent",
|
|
40849
|
+
border: "none",
|
|
40850
|
+
padding: 0,
|
|
40851
|
+
fontSize: 10,
|
|
40852
|
+
fontFamily: "inherit",
|
|
40853
|
+
color: selected ? color2 : GUI_THEME.dim,
|
|
40854
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
40855
|
+
opacity: disabled ? 0.4 : 1
|
|
40856
|
+
},
|
|
40857
|
+
children: [
|
|
40858
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40859
|
+
"span",
|
|
40860
|
+
{
|
|
40861
|
+
style: {
|
|
40862
|
+
width: 12,
|
|
40863
|
+
height: 12,
|
|
40864
|
+
borderRadius: "50%",
|
|
40865
|
+
border: `1px solid ${selected ? color2 : GUI_THEME.dim}`,
|
|
40866
|
+
display: "inline-flex",
|
|
40867
|
+
alignItems: "center",
|
|
40868
|
+
justifyContent: "center",
|
|
40869
|
+
flexShrink: 0
|
|
40870
|
+
},
|
|
40871
|
+
children: selected && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40872
|
+
"span",
|
|
40873
|
+
{
|
|
40874
|
+
style: {
|
|
40875
|
+
width: 6,
|
|
40876
|
+
height: 6,
|
|
40877
|
+
borderRadius: "50%",
|
|
40878
|
+
background: color2
|
|
40879
|
+
}
|
|
40880
|
+
}
|
|
40881
|
+
)
|
|
40882
|
+
}
|
|
40883
|
+
),
|
|
40884
|
+
label
|
|
40885
|
+
]
|
|
40886
|
+
}
|
|
40887
|
+
);
|
|
40888
|
+
var BoardSeriesGroup = ({
|
|
40889
|
+
showIssues,
|
|
40890
|
+
view,
|
|
40891
|
+
identities,
|
|
40892
|
+
hiddenIds,
|
|
40893
|
+
expanded,
|
|
40894
|
+
identitiesExpanded,
|
|
40895
|
+
filtered,
|
|
40896
|
+
onChangeShowIssues,
|
|
40897
|
+
onChangeView,
|
|
40898
|
+
onToggleIdentity,
|
|
40899
|
+
onOnlyIdentity,
|
|
40900
|
+
onToggleExpanded,
|
|
40901
|
+
onSetIdentitiesExpanded
|
|
40902
|
+
}) => {
|
|
40903
|
+
const sole = soleVisibleIdentity(identities, hiddenIds);
|
|
40904
|
+
const partial = filtered && hiddenIds.size > 0 && identityAxisFor(view) !== null;
|
|
40905
|
+
const label = sole ? `${VIEW_LABELS[view]}: ${sole.name}` : partial ? `${VIEW_LABELS[view]} (multi)` : VIEW_LABELS[view];
|
|
40906
|
+
const color2 = sole?.color ?? (partial ? GUI_THEME.dim2 : boardViewColor(view));
|
|
40907
|
+
const ref = useDismissOnOutsideClick(expanded, onToggleExpanded);
|
|
40908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
40909
|
+
"div",
|
|
40910
|
+
{
|
|
40911
|
+
ref,
|
|
40912
|
+
style: { position: "relative", display: "flex", flexDirection: "column" },
|
|
40913
|
+
children: [
|
|
40914
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
40915
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40916
|
+
Checkbox,
|
|
40917
|
+
{
|
|
40918
|
+
label: null,
|
|
40919
|
+
title: "Show board events",
|
|
40920
|
+
checked: showIssues,
|
|
40921
|
+
activeColor: color2,
|
|
40922
|
+
onChange: onChangeShowIssues
|
|
40923
|
+
}
|
|
40924
|
+
),
|
|
40925
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
40926
|
+
"button",
|
|
40927
|
+
{
|
|
40928
|
+
type: "button",
|
|
40929
|
+
onClick: onToggleExpanded,
|
|
40930
|
+
disabled: !showIssues,
|
|
40931
|
+
title: filtered ? "Choose what the board series plots" : "This window holds too many events to split by kind",
|
|
40932
|
+
"aria-haspopup": "listbox",
|
|
40933
|
+
"aria-expanded": expanded,
|
|
40934
|
+
style: selectTriggerStyle(color2, !showIssues),
|
|
40935
|
+
children: [
|
|
40936
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40937
|
+
"span",
|
|
40938
|
+
{
|
|
40939
|
+
style: {
|
|
40940
|
+
overflow: "hidden",
|
|
40941
|
+
textOverflow: "ellipsis",
|
|
40942
|
+
whiteSpace: "nowrap"
|
|
40943
|
+
},
|
|
40944
|
+
children: label
|
|
40945
|
+
}
|
|
40946
|
+
),
|
|
40947
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { style: { display: "inline-flex", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(IconChevronDown, { size: 12 }) })
|
|
40948
|
+
]
|
|
40949
|
+
}
|
|
40950
|
+
)
|
|
40951
|
+
] }),
|
|
40952
|
+
expanded && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { role: "radiogroup", style: popoverStyle, children: BOARD_VIEWS.map((option) => {
|
|
40953
|
+
const selected = view === option;
|
|
40954
|
+
const hasList = identityAxisFor(option) !== null;
|
|
40955
|
+
const open = selected && identitiesExpanded;
|
|
40956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
40957
|
+
"div",
|
|
40958
|
+
{
|
|
40959
|
+
style: { display: "flex", flexDirection: "column", gap: 7 },
|
|
40960
|
+
children: [
|
|
40961
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 3 }, children: [
|
|
40962
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40963
|
+
Radio,
|
|
40964
|
+
{
|
|
40965
|
+
label: VIEW_LABELS[option],
|
|
40966
|
+
selected,
|
|
40967
|
+
color: boardViewColor(option),
|
|
40968
|
+
disabled: !showIssues || !filtered,
|
|
40969
|
+
onSelect: () => onChangeView(option)
|
|
40970
|
+
}
|
|
40971
|
+
),
|
|
40972
|
+
hasList && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40973
|
+
"button",
|
|
40974
|
+
{
|
|
40975
|
+
type: "button",
|
|
40976
|
+
disabled: !showIssues || !filtered,
|
|
40977
|
+
onClick: () => {
|
|
40978
|
+
if (!selected) onChangeView(option);
|
|
40979
|
+
onSetIdentitiesExpanded(!open);
|
|
40980
|
+
},
|
|
40981
|
+
title: open ? "Hide the list" : "Pick which to show",
|
|
40982
|
+
"aria-expanded": open,
|
|
40983
|
+
style: disclosureStyle,
|
|
40984
|
+
children: open ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(IconChevronDown, { size: 12 }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(IconChevronRight, { size: 12 })
|
|
40985
|
+
}
|
|
40986
|
+
)
|
|
40987
|
+
] }),
|
|
40988
|
+
open && identities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40989
|
+
"div",
|
|
40990
|
+
{
|
|
40991
|
+
style: {
|
|
40992
|
+
...nestedListStyle,
|
|
40993
|
+
// A repo with dozens of tags would otherwise push the
|
|
40994
|
+
// board itself off the screen.
|
|
40995
|
+
maxHeight: 132,
|
|
40996
|
+
overflowY: "auto"
|
|
40997
|
+
},
|
|
40998
|
+
children: identities.map((identity) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
40999
|
+
"div",
|
|
41000
|
+
{
|
|
41001
|
+
style: {
|
|
41002
|
+
display: "flex",
|
|
41003
|
+
alignItems: "center",
|
|
41004
|
+
justifyContent: "space-between",
|
|
41005
|
+
gap: 10
|
|
41006
|
+
},
|
|
41007
|
+
children: [
|
|
41008
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
41009
|
+
Checkbox,
|
|
41010
|
+
{
|
|
41011
|
+
label: identity.name,
|
|
41012
|
+
checked: !hiddenIds.has(identity.id),
|
|
41013
|
+
activeColor: identity.color,
|
|
41014
|
+
disabled: !showIssues,
|
|
41015
|
+
onChange: (next) => onToggleIdentity(identity.id, next)
|
|
41016
|
+
}
|
|
41017
|
+
),
|
|
41018
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
41019
|
+
"button",
|
|
41020
|
+
{
|
|
41021
|
+
type: "button",
|
|
41022
|
+
title: `Show only ${identity.name}`,
|
|
41023
|
+
disabled: !showIssues,
|
|
41024
|
+
onClick: () => onOnlyIdentity(identity.id),
|
|
41025
|
+
style: onlyButtonStyle,
|
|
41026
|
+
children: "only"
|
|
41027
|
+
}
|
|
41028
|
+
)
|
|
41029
|
+
]
|
|
41030
|
+
},
|
|
41031
|
+
identity.id
|
|
41032
|
+
))
|
|
41033
|
+
}
|
|
41034
|
+
)
|
|
41035
|
+
]
|
|
41036
|
+
},
|
|
41037
|
+
option
|
|
41038
|
+
);
|
|
41039
|
+
}) })
|
|
41040
|
+
]
|
|
41041
|
+
}
|
|
41042
|
+
);
|
|
41043
|
+
};
|
|
40584
41044
|
var navButtonStyle = {
|
|
40585
41045
|
background: "transparent",
|
|
40586
41046
|
border: `1px solid ${GUI_THEME.dim}`,
|
|
@@ -40599,6 +41059,12 @@ var ScrubberControls = ({
|
|
|
40599
41059
|
showIssues,
|
|
40600
41060
|
showCommits,
|
|
40601
41061
|
allBoards,
|
|
41062
|
+
boardView,
|
|
41063
|
+
identities,
|
|
41064
|
+
hiddenIdentityIds,
|
|
41065
|
+
categoriesExpanded,
|
|
41066
|
+
identitiesExpanded,
|
|
41067
|
+
categoriesFiltered,
|
|
40602
41068
|
isScrubbing,
|
|
40603
41069
|
nowLabel,
|
|
40604
41070
|
onChangeScope,
|
|
@@ -40607,6 +41073,11 @@ var ScrubberControls = ({
|
|
|
40607
41073
|
onChangeShowIssues,
|
|
40608
41074
|
onChangeShowCommits,
|
|
40609
41075
|
onChangeAllBoards,
|
|
41076
|
+
onChangeBoardView,
|
|
41077
|
+
onToggleIdentity,
|
|
41078
|
+
onOnlyIdentity,
|
|
41079
|
+
onToggleCategoriesExpanded,
|
|
41080
|
+
onSetIdentitiesExpanded,
|
|
40610
41081
|
onReturnToLive
|
|
40611
41082
|
}) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
40612
41083
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
@@ -40682,16 +41153,7 @@ var ScrubberControls = ({
|
|
|
40682
41153
|
}
|
|
40683
41154
|
)
|
|
40684
41155
|
] }),
|
|
40685
|
-
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", gap:
|
|
40686
|
-
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40687
|
-
Checkbox,
|
|
40688
|
-
{
|
|
40689
|
-
label: "Board",
|
|
40690
|
-
checked: showIssues,
|
|
40691
|
-
activeColor: GUI_THEME.accent,
|
|
40692
|
-
onChange: onChangeShowIssues
|
|
40693
|
-
}
|
|
40694
|
-
),
|
|
41156
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
40695
41157
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40696
41158
|
Checkbox,
|
|
40697
41159
|
{
|
|
@@ -40702,12 +41164,21 @@ var ScrubberControls = ({
|
|
|
40702
41164
|
}
|
|
40703
41165
|
),
|
|
40704
41166
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40705
|
-
|
|
41167
|
+
BoardSeriesGroup,
|
|
40706
41168
|
{
|
|
40707
|
-
|
|
40708
|
-
|
|
40709
|
-
|
|
40710
|
-
|
|
41169
|
+
showIssues,
|
|
41170
|
+
view: boardView,
|
|
41171
|
+
identities,
|
|
41172
|
+
hiddenIds: hiddenIdentityIds,
|
|
41173
|
+
expanded: categoriesExpanded,
|
|
41174
|
+
identitiesExpanded,
|
|
41175
|
+
filtered: categoriesFiltered,
|
|
41176
|
+
onChangeShowIssues,
|
|
41177
|
+
onChangeView: onChangeBoardView,
|
|
41178
|
+
onToggleIdentity,
|
|
41179
|
+
onOnlyIdentity,
|
|
41180
|
+
onToggleExpanded: onToggleCategoriesExpanded,
|
|
41181
|
+
onSetIdentitiesExpanded
|
|
40711
41182
|
}
|
|
40712
41183
|
)
|
|
40713
41184
|
] }),
|
|
@@ -40881,7 +41352,11 @@ var SeriesLayer = ({
|
|
|
40881
41352
|
position: "absolute",
|
|
40882
41353
|
inset: 0,
|
|
40883
41354
|
pointerEvents: "none",
|
|
40884
|
-
animation: animate ? FADE_IN_ANIMATION : void 0
|
|
41355
|
+
animation: animate ? FADE_IN_ANIMATION : void 0,
|
|
41356
|
+
// Walls this subtree off from the needle and highlight moving above
|
|
41357
|
+
// it: without containment every frame of a drag re-lays-out the
|
|
41358
|
+
// thousands of dots inside, and the drag stutters.
|
|
41359
|
+
contain: "layout paint"
|
|
40885
41360
|
},
|
|
40886
41361
|
children
|
|
40887
41362
|
}
|
|
@@ -40942,47 +41417,6 @@ var VolumeBars = ({
|
|
|
40942
41417
|
))
|
|
40943
41418
|
] });
|
|
40944
41419
|
};
|
|
40945
|
-
var ScatterDot = ({
|
|
40946
|
-
fraction,
|
|
40947
|
-
hourFraction,
|
|
40948
|
-
size,
|
|
40949
|
-
color: color2,
|
|
40950
|
-
opacity,
|
|
40951
|
-
zIndex,
|
|
40952
|
-
title,
|
|
40953
|
-
animation,
|
|
40954
|
-
interactive = true,
|
|
40955
|
-
onMouseEnter,
|
|
40956
|
-
onMouseLeave,
|
|
40957
|
-
onClick
|
|
40958
|
-
}) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
40959
|
-
"div",
|
|
40960
|
-
{
|
|
40961
|
-
title,
|
|
40962
|
-
onPointerDown: onClick && ((event) => event.stopPropagation()),
|
|
40963
|
-
onClick: onClick && ((event) => {
|
|
40964
|
-
event.stopPropagation();
|
|
40965
|
-
onClick();
|
|
40966
|
-
}),
|
|
40967
|
-
onMouseEnter,
|
|
40968
|
-
onMouseLeave,
|
|
40969
|
-
style: {
|
|
40970
|
-
position: "absolute",
|
|
40971
|
-
left: `${fraction * 100}%`,
|
|
40972
|
-
top: EVENTS_MODE_VERTICAL_PADDING + hourFraction * EVENTS_SCATTER_HEIGHT,
|
|
40973
|
-
width: size,
|
|
40974
|
-
height: size,
|
|
40975
|
-
borderRadius: "50%",
|
|
40976
|
-
background: color2,
|
|
40977
|
-
opacity,
|
|
40978
|
-
zIndex,
|
|
40979
|
-
transform: `translate(${-size / 2}px, -50%)`,
|
|
40980
|
-
animation,
|
|
40981
|
-
pointerEvents: interactive ? "auto" : "none",
|
|
40982
|
-
cursor: onClick ? "pointer" : void 0
|
|
40983
|
-
}
|
|
40984
|
-
}
|
|
40985
|
-
);
|
|
40986
41420
|
var ScrubberNeedle = ({ fraction }) => {
|
|
40987
41421
|
const [hovered, setHovered] = (0, import_react14.useState)(false);
|
|
40988
41422
|
const hover = {
|
|
@@ -41102,10 +41536,175 @@ var ScrubberHoverHint = ({
|
|
|
41102
41536
|
]
|
|
41103
41537
|
}
|
|
41104
41538
|
);
|
|
41539
|
+
var HIT_RADIUS = 7;
|
|
41540
|
+
var pointAt = (layers, x, y, width) => {
|
|
41541
|
+
let best = null;
|
|
41542
|
+
let bestDistance = HIT_RADIUS * HIT_RADIUS;
|
|
41543
|
+
for (const layer of layers) {
|
|
41544
|
+
if (layer.leaving) continue;
|
|
41545
|
+
for (const point4 of layer.points) {
|
|
41546
|
+
const dx = point4.fraction * width - x;
|
|
41547
|
+
const dy = EVENTS_MODE_VERTICAL_PADDING + point4.hourFraction * EVENTS_SCATTER_HEIGHT - y;
|
|
41548
|
+
const distance = dx * dx + dy * dy;
|
|
41549
|
+
if (distance <= bestDistance) {
|
|
41550
|
+
bestDistance = distance;
|
|
41551
|
+
best = point4;
|
|
41552
|
+
}
|
|
41553
|
+
}
|
|
41554
|
+
}
|
|
41555
|
+
return best;
|
|
41556
|
+
};
|
|
41557
|
+
var ScatterCanvas = ({
|
|
41558
|
+
layers,
|
|
41559
|
+
animate,
|
|
41560
|
+
onPointEnter,
|
|
41561
|
+
onPointLeave,
|
|
41562
|
+
onInspectCommit
|
|
41563
|
+
}) => {
|
|
41564
|
+
const canvasRef = (0, import_react14.useRef)(null);
|
|
41565
|
+
const sizeRef = (0, import_react14.useRef)({ width: 0, height: 0 });
|
|
41566
|
+
const layersRef = (0, import_react14.useRef)(layers);
|
|
41567
|
+
layersRef.current = layers;
|
|
41568
|
+
const phasesRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
|
|
41569
|
+
const frameRef = (0, import_react14.useRef)(null);
|
|
41570
|
+
const hoveredRef = (0, import_react14.useRef)(null);
|
|
41571
|
+
const [entrancePlaying, setEntrancePlaying] = (0, import_react14.useState)(false);
|
|
41572
|
+
const paint = (0, import_react14.useCallback)((now) => {
|
|
41573
|
+
const canvas = canvasRef.current;
|
|
41574
|
+
const context = canvas?.getContext("2d");
|
|
41575
|
+
if (!canvas || !context) return false;
|
|
41576
|
+
const { width, height } = sizeRef.current;
|
|
41577
|
+
context.clearRect(0, 0, width, height);
|
|
41578
|
+
let running = false;
|
|
41579
|
+
for (const layer of layersRef.current) {
|
|
41580
|
+
const phase = phasesRef.current.get(layer.id);
|
|
41581
|
+
const elapsed = phase ? now - phase.startedAt : null;
|
|
41582
|
+
const done = elapsed === null || elapsed >= DOT_EXIT_TOTAL_MS;
|
|
41583
|
+
if (phase && !done) running = true;
|
|
41584
|
+
if (phase?.mode === "out" && done) continue;
|
|
41585
|
+
for (const point4 of layer.points) {
|
|
41586
|
+
const scale = phase === null || phase === void 0 || done ? 1 : phase.mode === "in" ? dotEntranceScale(point4.key, elapsed) : dotExitScale(point4.key, elapsed);
|
|
41587
|
+
if (scale <= 0) continue;
|
|
41588
|
+
context.globalAlpha = point4.opacity;
|
|
41589
|
+
context.fillStyle = point4.color;
|
|
41590
|
+
context.beginPath();
|
|
41591
|
+
context.arc(
|
|
41592
|
+
point4.fraction * width,
|
|
41593
|
+
EVENTS_MODE_VERTICAL_PADDING + point4.hourFraction * EVENTS_SCATTER_HEIGHT,
|
|
41594
|
+
point4.radius * scale,
|
|
41595
|
+
0,
|
|
41596
|
+
Math.PI * 2
|
|
41597
|
+
);
|
|
41598
|
+
context.fill();
|
|
41599
|
+
}
|
|
41600
|
+
}
|
|
41601
|
+
context.globalAlpha = 1;
|
|
41602
|
+
return running;
|
|
41603
|
+
}, []);
|
|
41604
|
+
const run = (0, import_react14.useCallback)(() => {
|
|
41605
|
+
if (frameRef.current !== null) return;
|
|
41606
|
+
const step = () => {
|
|
41607
|
+
frameRef.current = null;
|
|
41608
|
+
const running = paint(performance.now());
|
|
41609
|
+
if (running) frameRef.current = requestAnimationFrame(step);
|
|
41610
|
+
else setEntrancePlaying(false);
|
|
41611
|
+
};
|
|
41612
|
+
frameRef.current = requestAnimationFrame(step);
|
|
41613
|
+
}, [paint]);
|
|
41614
|
+
(0, import_react14.useEffect)(() => {
|
|
41615
|
+
const canvas = canvasRef.current;
|
|
41616
|
+
const parent = canvas?.parentElement;
|
|
41617
|
+
if (!canvas || !parent) return;
|
|
41618
|
+
const resize = () => {
|
|
41619
|
+
const ratio = window.devicePixelRatio || 1;
|
|
41620
|
+
const { width, height } = parent.getBoundingClientRect();
|
|
41621
|
+
sizeRef.current = { width, height };
|
|
41622
|
+
canvas.width = Math.round(width * ratio);
|
|
41623
|
+
canvas.height = Math.round(height * ratio);
|
|
41624
|
+
canvas.style.width = `${width}px`;
|
|
41625
|
+
canvas.style.height = `${height}px`;
|
|
41626
|
+
canvas.getContext("2d")?.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
41627
|
+
paint(performance.now());
|
|
41628
|
+
};
|
|
41629
|
+
resize();
|
|
41630
|
+
const observer = new ResizeObserver(resize);
|
|
41631
|
+
observer.observe(parent);
|
|
41632
|
+
return () => observer.disconnect();
|
|
41633
|
+
}, [paint]);
|
|
41634
|
+
const signature = layers.map((layer) => `${layer.id}:${layer.generation}:${layer.leaving}`).join("|");
|
|
41635
|
+
(0, import_react14.useEffect)(() => {
|
|
41636
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41637
|
+
for (const layer of layers) {
|
|
41638
|
+
seen.add(layer.id);
|
|
41639
|
+
const key = `${layer.generation}:${layer.leaving}`;
|
|
41640
|
+
const marker = `${layer.id}@${key}`;
|
|
41641
|
+
const current = phasesRef.current.get(layer.id);
|
|
41642
|
+
if (current?.marker === marker)
|
|
41643
|
+
continue;
|
|
41644
|
+
if (!animate) {
|
|
41645
|
+
phasesRef.current.delete(layer.id);
|
|
41646
|
+
continue;
|
|
41647
|
+
}
|
|
41648
|
+
phasesRef.current.set(layer.id, {
|
|
41649
|
+
mode: layer.leaving ? "out" : "in",
|
|
41650
|
+
startedAt: performance.now(),
|
|
41651
|
+
marker
|
|
41652
|
+
});
|
|
41653
|
+
if (!layer.leaving) setEntrancePlaying(true);
|
|
41654
|
+
}
|
|
41655
|
+
for (const id of [...phasesRef.current.keys()])
|
|
41656
|
+
if (!seen.has(id)) phasesRef.current.delete(id);
|
|
41657
|
+
if (animate) run();
|
|
41658
|
+
else paint(performance.now());
|
|
41659
|
+
}, [signature, animate, run, paint]);
|
|
41660
|
+
(0, import_react14.useEffect)(() => {
|
|
41661
|
+
if (frameRef.current === null) paint(performance.now());
|
|
41662
|
+
}, [layers, paint]);
|
|
41663
|
+
(0, import_react14.useEffect)(
|
|
41664
|
+
() => () => {
|
|
41665
|
+
if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
|
|
41666
|
+
},
|
|
41667
|
+
[]
|
|
41668
|
+
);
|
|
41669
|
+
const hitTest = (event) => {
|
|
41670
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
41671
|
+
return pointAt(
|
|
41672
|
+
layersRef.current,
|
|
41673
|
+
event.clientX - rect.left,
|
|
41674
|
+
event.clientY - rect.top,
|
|
41675
|
+
rect.width
|
|
41676
|
+
);
|
|
41677
|
+
};
|
|
41678
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
41679
|
+
"canvas",
|
|
41680
|
+
{
|
|
41681
|
+
ref: canvasRef,
|
|
41682
|
+
"data-entrance": entrancePlaying ? "playing" : "done",
|
|
41683
|
+
style: { position: "absolute", inset: 0 },
|
|
41684
|
+
onMouseMove: (event) => {
|
|
41685
|
+
const point4 = hitTest(event);
|
|
41686
|
+
if (point4?.key === hoveredRef.current) return;
|
|
41687
|
+
hoveredRef.current = point4?.key ?? null;
|
|
41688
|
+
if (point4) onPointEnter(point4);
|
|
41689
|
+
else onPointLeave();
|
|
41690
|
+
},
|
|
41691
|
+
onMouseLeave: () => {
|
|
41692
|
+
hoveredRef.current = null;
|
|
41693
|
+
onPointLeave();
|
|
41694
|
+
},
|
|
41695
|
+
onPointerDown: (event) => {
|
|
41696
|
+
if (hitTest(event)?.commitSha) event.stopPropagation();
|
|
41697
|
+
},
|
|
41698
|
+
onClick: (event) => {
|
|
41699
|
+
const sha = hitTest(event)?.commitSha;
|
|
41700
|
+
if (sha) onInspectCommit(sha);
|
|
41701
|
+
}
|
|
41702
|
+
}
|
|
41703
|
+
);
|
|
41704
|
+
};
|
|
41105
41705
|
|
|
41106
41706
|
// source/gui/client/components/ScrubberLayout.tsx
|
|
41107
41707
|
var import_jsx_runtime31 = __toESM(require_jsx_runtime(), 1);
|
|
41108
|
-
var dotAnimation = (key, animate, leaving) => !animate ? void 0 : leaving ? dotExitAnimation(key) : dotAppearAnimation(key);
|
|
41109
41708
|
var ScrubberLayout = ({
|
|
41110
41709
|
collapsed,
|
|
41111
41710
|
onToggleCollapsed,
|
|
@@ -41211,7 +41810,7 @@ var ScrubberLayout = ({
|
|
|
41211
41810
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
41212
41811
|
TrackBaseline,
|
|
41213
41812
|
{
|
|
41214
|
-
color:
|
|
41813
|
+
color: chart.issueSeriesColor,
|
|
41215
41814
|
anchor: layoutMode === "even" ? "bottom" : "centre"
|
|
41216
41815
|
}
|
|
41217
41816
|
),
|
|
@@ -41224,70 +41823,20 @@ var ScrubberLayout = ({
|
|
|
41224
41823
|
firstBar: chart.issueBarRange[0],
|
|
41225
41824
|
lastBar: chart.issueBarRange[1],
|
|
41226
41825
|
highlightedIndex: chart.hoveredBucketIndex,
|
|
41227
|
-
color:
|
|
41826
|
+
color: chart.issueSeriesColor,
|
|
41228
41827
|
direction: "up",
|
|
41229
41828
|
animate
|
|
41230
41829
|
}
|
|
41231
41830
|
) }, `issues-${windowKey}`),
|
|
41232
|
-
|
|
41233
|
-
|
|
41234
|
-
{
|
|
41235
|
-
animate: animate && !chart.issueScatter.leaving,
|
|
41236
|
-
children: chart.eventBuckets.map((bucket) => {
|
|
41237
|
-
const intensity = bucket.count / chart.maxEventCount;
|
|
41238
|
-
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
41239
|
-
ScatterDot,
|
|
41240
|
-
{
|
|
41241
|
-
fraction: axis.fractionForTime(bucket.t),
|
|
41242
|
-
hourFraction: hourFractionForTime(bucket.t),
|
|
41243
|
-
size: 3 + intensity * 6,
|
|
41244
|
-
color: GUI_THEME.accent,
|
|
41245
|
-
opacity: 0.3 + intensity * 0.5,
|
|
41246
|
-
zIndex: 2,
|
|
41247
|
-
title: `${bucket.count} change${bucket.count === 1 ? "" : "s"}, ${formatDateTime(new Date(bucket.t))}`,
|
|
41248
|
-
animation: dotAnimation(
|
|
41249
|
-
String(bucket.t),
|
|
41250
|
-
animate,
|
|
41251
|
-
chart.issueScatter.leaving
|
|
41252
|
-
),
|
|
41253
|
-
interactive: !chart.issueScatter.leaving,
|
|
41254
|
-
onMouseEnter: () => on.onIssueDotEnter(bucket),
|
|
41255
|
-
onMouseLeave: on.onIssueDotLeave
|
|
41256
|
-
},
|
|
41257
|
-
bucket.t
|
|
41258
|
-
);
|
|
41259
|
-
})
|
|
41260
|
-
},
|
|
41261
|
-
`issues-${windowKey}`
|
|
41262
|
-
),
|
|
41263
|
-
chart.commitScatter.mounted && layoutMode === "real" && chart.commits.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
41264
|
-
SeriesLayer,
|
|
41831
|
+
layoutMode === "real" && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
41832
|
+
ScatterCanvas,
|
|
41265
41833
|
{
|
|
41266
|
-
|
|
41267
|
-
|
|
41268
|
-
|
|
41269
|
-
|
|
41270
|
-
|
|
41271
|
-
|
|
41272
|
-
size: 4,
|
|
41273
|
-
color: GUI_THEME.green,
|
|
41274
|
-
opacity: chart.hoveredCommitSha === commit.sha ? 1 : 0.55,
|
|
41275
|
-
zIndex: 1,
|
|
41276
|
-
title: `${formatDateTime(new Date(commit.time))} \u2014 ${commit.subject} \u2014 ${commit.author} (${commit.linesChanged.toLocaleString()} lines)`,
|
|
41277
|
-
animation: dotAnimation(
|
|
41278
|
-
commit.sha,
|
|
41279
|
-
animate,
|
|
41280
|
-
chart.commitScatter.leaving
|
|
41281
|
-
),
|
|
41282
|
-
interactive: !chart.commitScatter.leaving,
|
|
41283
|
-
onClick: () => on.onInspectCommit(commit.sha),
|
|
41284
|
-
onMouseEnter: () => on.onCommitDotEnter(commit),
|
|
41285
|
-
onMouseLeave: on.onCommitDotLeave
|
|
41286
|
-
},
|
|
41287
|
-
commit.sha
|
|
41288
|
-
))
|
|
41289
|
-
},
|
|
41290
|
-
`commits-${windowKey}`
|
|
41834
|
+
layers: chart.scatterLayers,
|
|
41835
|
+
animate,
|
|
41836
|
+
onPointEnter: on.onScatterPointEnter,
|
|
41837
|
+
onPointLeave: on.onScatterPointLeave,
|
|
41838
|
+
onInspectCommit: on.onInspectCommit
|
|
41839
|
+
}
|
|
41291
41840
|
)
|
|
41292
41841
|
]
|
|
41293
41842
|
}
|
|
@@ -41329,7 +41878,7 @@ var ScrubberLayout = ({
|
|
|
41329
41878
|
{
|
|
41330
41879
|
...chart.boardHint,
|
|
41331
41880
|
segmentLabel: chart.hoveredSegment?.label,
|
|
41332
|
-
stripeColor:
|
|
41881
|
+
stripeColor: chart.issueSeriesColor,
|
|
41333
41882
|
trackWidthPx: chart.trackWidthPx
|
|
41334
41883
|
}
|
|
41335
41884
|
),
|
|
@@ -41356,11 +41905,29 @@ var ScrubberLayout = ({
|
|
|
41356
41905
|
// source/gui/client/components/TimeScrubber.tsx
|
|
41357
41906
|
var import_jsx_runtime32 = __toESM(require_jsx_runtime(), 1);
|
|
41358
41907
|
var SCRUB_THROTTLE_MS = 120;
|
|
41908
|
+
var boardEventRow = (count) => `${count} board event${count === 1 ? "" : "s"}`;
|
|
41359
41909
|
var COLLAPSED_STORAGE_KEY = "epiq.timeScrubber.collapsed";
|
|
41360
41910
|
var SCOPE_STORAGE_KEY = "epiq.timeScrubber.scope";
|
|
41361
41911
|
var SHOW_ISSUES_STORAGE_KEY = "epiq.timeScrubber.showIssues";
|
|
41362
41912
|
var SHOW_COMMITS_STORAGE_KEY = "epiq.timeScrubber.showCommits";
|
|
41363
41913
|
var ALL_BOARDS_STORAGE_KEY = "epiq.timeScrubber.allBoards";
|
|
41914
|
+
var BOARD_VIEW_STORAGE_KEY = "epiq.timeScrubber.boardView";
|
|
41915
|
+
var HIDDEN_IDENTITIES_STORAGE_KEY = "epiq.timeScrubber.hiddenIdentities";
|
|
41916
|
+
var CATEGORIES_EXPANDED_STORAGE_KEY = "epiq.timeScrubber.categoriesExpanded";
|
|
41917
|
+
var IDENTITIES_EXPANDED_STORAGE_KEY = "epiq.timeScrubber.identitiesExpanded";
|
|
41918
|
+
var readStoredBoardView = () => {
|
|
41919
|
+
const stored = localStorage.getItem(BOARD_VIEW_STORAGE_KEY);
|
|
41920
|
+
return isBoardView(stored) ? stored : "all";
|
|
41921
|
+
};
|
|
41922
|
+
var readStoredHiddenIds = () => {
|
|
41923
|
+
try {
|
|
41924
|
+
const stored = localStorage.getItem(HIDDEN_IDENTITIES_STORAGE_KEY);
|
|
41925
|
+
const parsed = stored ? JSON.parse(stored) : [];
|
|
41926
|
+
return new Set(Array.isArray(parsed) ? parsed.map(String) : []);
|
|
41927
|
+
} catch {
|
|
41928
|
+
return /* @__PURE__ */ new Set();
|
|
41929
|
+
}
|
|
41930
|
+
};
|
|
41364
41931
|
var readStoredScope = () => {
|
|
41365
41932
|
const stored = localStorage.getItem(SCOPE_STORAGE_KEY);
|
|
41366
41933
|
return isScope(stored) ? stored : "all";
|
|
@@ -41375,7 +41942,8 @@ var TimeScrubber = ({
|
|
|
41375
41942
|
onRequestHistory,
|
|
41376
41943
|
boardId,
|
|
41377
41944
|
connected,
|
|
41378
|
-
onInspectCommit
|
|
41945
|
+
onInspectCommit,
|
|
41946
|
+
onBoardFilterChange
|
|
41379
41947
|
}) => {
|
|
41380
41948
|
const animate = !usePrefersReducedMotion();
|
|
41381
41949
|
const trackRef = (0, import_react15.useRef)(null);
|
|
@@ -41396,6 +41964,16 @@ var TimeScrubber = ({
|
|
|
41396
41964
|
SHOW_COMMITS_STORAGE_KEY,
|
|
41397
41965
|
true
|
|
41398
41966
|
);
|
|
41967
|
+
const [boardView, setBoardView] = (0, import_react15.useState)(readStoredBoardView);
|
|
41968
|
+
const [hiddenIdentityIds, setHiddenIdentityIds] = (0, import_react15.useState)(readStoredHiddenIds);
|
|
41969
|
+
const [categoriesExpanded, setCategoriesExpanded] = usePersistedFlag(
|
|
41970
|
+
CATEGORIES_EXPANDED_STORAGE_KEY,
|
|
41971
|
+
false
|
|
41972
|
+
);
|
|
41973
|
+
const [identitiesExpanded, setIdentitiesExpanded] = usePersistedFlag(
|
|
41974
|
+
IDENTITIES_EXPANDED_STORAGE_KEY,
|
|
41975
|
+
false
|
|
41976
|
+
);
|
|
41399
41977
|
const [allBoards, setAllBoards] = usePersistedFlag(
|
|
41400
41978
|
ALL_BOARDS_STORAGE_KEY,
|
|
41401
41979
|
false
|
|
@@ -41425,19 +42003,124 @@ var TimeScrubber = ({
|
|
|
41425
42003
|
armEntrance();
|
|
41426
42004
|
setOffset(nextOffset);
|
|
41427
42005
|
};
|
|
42006
|
+
const changeBoardView = (next) => {
|
|
42007
|
+
setBoardView(next);
|
|
42008
|
+
localStorage.setItem(BOARD_VIEW_STORAGE_KEY, next);
|
|
42009
|
+
};
|
|
42010
|
+
const isolateIdentity = (id) => {
|
|
42011
|
+
const others = identities.filter((identity) => identity.id !== id).map((identity) => identity.id);
|
|
42012
|
+
setHiddenIdentityIds((previous3) => {
|
|
42013
|
+
const isolated = !previous3.has(id) && others.length === previous3.size && others.every((other) => previous3.has(other));
|
|
42014
|
+
const hidden = isolated ? /* @__PURE__ */ new Set() : new Set(others);
|
|
42015
|
+
localStorage.setItem(
|
|
42016
|
+
HIDDEN_IDENTITIES_STORAGE_KEY,
|
|
42017
|
+
JSON.stringify([...hidden])
|
|
42018
|
+
);
|
|
42019
|
+
return hidden;
|
|
42020
|
+
});
|
|
42021
|
+
};
|
|
42022
|
+
const toggleIdentity = (id, next) => {
|
|
42023
|
+
setHiddenIdentityIds((previous3) => {
|
|
42024
|
+
const hidden = new Set(previous3);
|
|
42025
|
+
if (next) {
|
|
42026
|
+
hidden.delete(id);
|
|
42027
|
+
} else {
|
|
42028
|
+
hidden.add(id);
|
|
42029
|
+
}
|
|
42030
|
+
localStorage.setItem(
|
|
42031
|
+
HIDDEN_IDENTITIES_STORAGE_KEY,
|
|
42032
|
+
JSON.stringify([...hidden])
|
|
42033
|
+
);
|
|
42034
|
+
return hidden;
|
|
42035
|
+
});
|
|
42036
|
+
};
|
|
41428
42037
|
const changeAllBoards = (next) => {
|
|
41429
42038
|
armEntrance();
|
|
41430
42039
|
setAllBoards(next);
|
|
41431
42040
|
};
|
|
41432
|
-
const
|
|
41433
|
-
|
|
41434
|
-
const
|
|
41435
|
-
|
|
41436
|
-
|
|
41437
|
-
|
|
41438
|
-
|
|
41439
|
-
|
|
42041
|
+
const steadyBoardId = (0, import_react15.useRef)(boardId);
|
|
42042
|
+
if (boardId !== null) steadyBoardId.current = boardId;
|
|
42043
|
+
const requestKey = (0, import_react15.useMemo)(
|
|
42044
|
+
() => JSON.stringify([scope, offset, steadyBoardId.current, allBoards]),
|
|
42045
|
+
[scope, offset, steadyBoardId.current, allBoards]
|
|
42046
|
+
);
|
|
42047
|
+
const filterKey = (0, import_react15.useMemo)(
|
|
42048
|
+
() => JSON.stringify([boardView, [...hiddenIdentityIds].sort()]),
|
|
42049
|
+
[boardView, hiddenIdentityIds]
|
|
42050
|
+
);
|
|
42051
|
+
const shownRef = (0, import_react15.useRef)({ timeline, commits });
|
|
42052
|
+
const heldKey = (0, import_react15.useRef)(null);
|
|
42053
|
+
const heldHistoryId = (0, import_react15.useRef)(null);
|
|
42054
|
+
const awaiting = (0, import_react15.useRef)(true);
|
|
42055
|
+
if (heldKey.current !== requestKey) {
|
|
42056
|
+
heldKey.current = requestKey;
|
|
42057
|
+
awaiting.current = true;
|
|
42058
|
+
}
|
|
42059
|
+
if (awaiting.current && heldHistoryId.current !== historyId) {
|
|
42060
|
+
heldHistoryId.current = historyId;
|
|
42061
|
+
shownRef.current = { timeline, commits };
|
|
42062
|
+
awaiting.current = false;
|
|
42063
|
+
}
|
|
42064
|
+
const shown = shownRef.current;
|
|
42065
|
+
const axis = (0, import_react15.useMemo)(() => buildAxis(shown.timeline, shown.commits), [shown]);
|
|
42066
|
+
const categoriesFiltered = (timeline?.events.length ?? 0) > 0;
|
|
42067
|
+
const identities = (0, import_react15.useMemo)(
|
|
42068
|
+
() => listIdentities(shown.timeline, boardView),
|
|
42069
|
+
[shown, boardView]
|
|
42070
|
+
);
|
|
42071
|
+
const soleIdentity = (0, import_react15.useMemo)(
|
|
42072
|
+
() => soleVisibleIdentity(identities, hiddenIdentityIds),
|
|
42073
|
+
[identities, hiddenIdentityIds]
|
|
42074
|
+
);
|
|
42075
|
+
const issueCounts = bucketIssueCounts(
|
|
42076
|
+
axis,
|
|
42077
|
+
shown.timeline,
|
|
42078
|
+
boardView,
|
|
42079
|
+
hiddenIdentityIds
|
|
42080
|
+
);
|
|
42081
|
+
const commitStats = bucketCommitStats(axis, shown.commits);
|
|
42082
|
+
const liveEventDots = (0, import_react15.useMemo)(
|
|
42083
|
+
() => buildEventDots(shown.timeline, boardView, hiddenIdentityIds),
|
|
42084
|
+
[shown, boardView, hiddenIdentityIds]
|
|
42085
|
+
);
|
|
42086
|
+
const dragging = dragFraction !== null;
|
|
42087
|
+
const commitPoints = (0, import_react15.useMemo)(
|
|
42088
|
+
() => shown.commits.map((commit) => ({
|
|
42089
|
+
key: commit.sha,
|
|
42090
|
+
t: commit.time,
|
|
42091
|
+
fraction: axis.fractionForTime(commit.time),
|
|
42092
|
+
hourFraction: hourFractionForTime(commit.time),
|
|
42093
|
+
radius: 2,
|
|
42094
|
+
color: GUI_THEME.green,
|
|
42095
|
+
opacity: 0.55,
|
|
42096
|
+
// The hint labels the moment itself, so this is the rest of it.
|
|
42097
|
+
title: `${commit.subject} \u2014 ${commit.author} (${commit.linesChanged.toLocaleString()} lines)`,
|
|
42098
|
+
commitSha: commit.sha
|
|
42099
|
+
})),
|
|
42100
|
+
[shown.commits, axis]
|
|
42101
|
+
);
|
|
42102
|
+
const issuePoints = (0, import_react15.useMemo)(
|
|
42103
|
+
() => liveEventDots.map((dot) => ({
|
|
42104
|
+
key: dot.key,
|
|
42105
|
+
t: dot.t,
|
|
42106
|
+
fraction: axis.fractionForTime(dot.t),
|
|
42107
|
+
hourFraction: hourFractionForTime(dot.t),
|
|
42108
|
+
radius: dot.size / 2,
|
|
42109
|
+
color: dot.color,
|
|
42110
|
+
opacity: dot.opacity,
|
|
42111
|
+
title: dotDetail(dot),
|
|
42112
|
+
commitSha: null
|
|
42113
|
+
})),
|
|
42114
|
+
[liveEventDots, axis]
|
|
42115
|
+
);
|
|
42116
|
+
const boardFilter = (0, import_react15.useMemo)(
|
|
42117
|
+
() => buildBoardFilter(boardView, identities, hiddenIdentityIds),
|
|
42118
|
+
[boardView, identities, hiddenIdentityIds]
|
|
41440
42119
|
);
|
|
42120
|
+
(0, import_react15.useEffect)(() => {
|
|
42121
|
+
onBoardFilterChange(boardFilter);
|
|
42122
|
+
}, [boardFilter]);
|
|
42123
|
+
const maxIssueBucketCount = maxOf(issueCounts, 1);
|
|
41441
42124
|
const maxCommitCount = maxOf(
|
|
41442
42125
|
Array.from(commitStats.values(), (stats) => stats.count),
|
|
41443
42126
|
1
|
|
@@ -41455,7 +42138,12 @@ var TimeScrubber = ({
|
|
|
41455
42138
|
const armEntrance = () => {
|
|
41456
42139
|
armed.current = true;
|
|
41457
42140
|
};
|
|
41458
|
-
(0, import_react15.
|
|
42141
|
+
const armedBoardId = (0, import_react15.useRef)(boardId);
|
|
42142
|
+
(0, import_react15.useEffect)(() => {
|
|
42143
|
+
if (boardId === null || boardId === armedBoardId.current) return;
|
|
42144
|
+
armedBoardId.current = boardId;
|
|
42145
|
+
armEntrance();
|
|
42146
|
+
}, [boardId]);
|
|
41459
42147
|
(0, import_react15.useEffect)(() => {
|
|
41460
42148
|
if (seenHistoryId.current === historyId) return;
|
|
41461
42149
|
seenHistoryId.current = historyId;
|
|
@@ -41463,7 +42151,28 @@ var TimeScrubber = ({
|
|
|
41463
42151
|
armed.current = false;
|
|
41464
42152
|
setEntrance((generation) => generation + 1);
|
|
41465
42153
|
}, [historyId]);
|
|
41466
|
-
const windowKey = `${layoutMode}-${entrance}`;
|
|
42154
|
+
const windowKey = `${layoutMode}-${entrance}-${filterKey}`;
|
|
42155
|
+
const scatterLayers = (0, import_react15.useMemo)(
|
|
42156
|
+
() => [
|
|
42157
|
+
...commitScatter.mounted ? [
|
|
42158
|
+
{
|
|
42159
|
+
id: "commits",
|
|
42160
|
+
points: commitPoints,
|
|
42161
|
+
generation: windowKey,
|
|
42162
|
+
leaving: commitScatter.leaving
|
|
42163
|
+
}
|
|
42164
|
+
] : [],
|
|
42165
|
+
...issueScatter.mounted ? [
|
|
42166
|
+
{
|
|
42167
|
+
id: "issues",
|
|
42168
|
+
points: issuePoints,
|
|
42169
|
+
generation: windowKey,
|
|
42170
|
+
leaving: issueScatter.leaving
|
|
42171
|
+
}
|
|
42172
|
+
] : []
|
|
42173
|
+
],
|
|
42174
|
+
[commitPoints, issuePoints, commitScatter, issueScatter, windowKey]
|
|
42175
|
+
);
|
|
41467
42176
|
const confirmedFraction = timeTravel.mode === "scrub" && timeTravel.asOfTime !== null ? axis.fractionForTime(timeTravel.asOfTime) : 1;
|
|
41468
42177
|
const fractionFromClientX = (clientX) => {
|
|
41469
42178
|
const track = trackRef.current;
|
|
@@ -41493,7 +42202,30 @@ var TimeScrubber = ({
|
|
|
41493
42202
|
);
|
|
41494
42203
|
const bucketTimeAt = (index2) => index2 !== null && index2 < axis.bucketCount ? axis.bucketTimeAt(index2) : void 0;
|
|
41495
42204
|
const centreFraction = (index2) => (index2 + 0.5) / axis.bucketCount;
|
|
41496
|
-
const
|
|
42205
|
+
const onScatterPointEnter = (0, import_react15.useCallback)(
|
|
42206
|
+
(point4) => {
|
|
42207
|
+
if (point4.commitSha) {
|
|
42208
|
+
const commit = shown.commits.find((c) => c.sha === point4.commitSha);
|
|
42209
|
+
if (commit) {
|
|
42210
|
+
setHoveredCommit({ commit, fraction: point4.fraction });
|
|
42211
|
+
setHoveredEvent(null);
|
|
42212
|
+
}
|
|
42213
|
+
return;
|
|
42214
|
+
}
|
|
42215
|
+
setHoveredCommit(null);
|
|
42216
|
+
setHoveredEvent({
|
|
42217
|
+
label: formatDateTime(new Date(point4.t)),
|
|
42218
|
+
detail: point4.title,
|
|
42219
|
+
t: point4.t,
|
|
42220
|
+
fraction: point4.fraction
|
|
42221
|
+
});
|
|
42222
|
+
},
|
|
42223
|
+
[shown.commits]
|
|
42224
|
+
);
|
|
42225
|
+
const onScatterPointLeave = (0, import_react15.useCallback)(() => {
|
|
42226
|
+
setHoveredEvent(null);
|
|
42227
|
+
setHoveredCommit(null);
|
|
42228
|
+
}, []);
|
|
41497
42229
|
const hoveredBucketTime = bucketTimeAt(hoveredBucketIndex);
|
|
41498
42230
|
const hoveredBucketCount = hoveredBucketIndex !== null ? issueCounts[hoveredBucketIndex] : void 0;
|
|
41499
42231
|
const boardHint = layoutMode === "even" ? hoveredBucketTime !== void 0 && hoveredBucketCount !== void 0 ? {
|
|
@@ -41506,7 +42238,7 @@ var TimeScrubber = ({
|
|
|
41506
42238
|
empty: hoveredBucketCount === 0
|
|
41507
42239
|
} : null : hoveredEvent ? {
|
|
41508
42240
|
label: hoveredEvent.label,
|
|
41509
|
-
rows: [
|
|
42241
|
+
rows: [hoveredEvent.detail],
|
|
41510
42242
|
fraction: hoveredEvent.fraction
|
|
41511
42243
|
} : null;
|
|
41512
42244
|
const hoveredCommitStats = hoveredCommitBucketIndex !== null ? commitStats.get(hoveredCommitBucketIndex) : void 0;
|
|
@@ -41557,6 +42289,17 @@ var TimeScrubber = ({
|
|
|
41557
42289
|
onChangeShowIssues: setShowIssues,
|
|
41558
42290
|
onChangeShowCommits: setShowCommits,
|
|
41559
42291
|
onChangeAllBoards: changeAllBoards,
|
|
42292
|
+
boardView,
|
|
42293
|
+
identities,
|
|
42294
|
+
hiddenIdentityIds,
|
|
42295
|
+
categoriesExpanded,
|
|
42296
|
+
identitiesExpanded,
|
|
42297
|
+
categoriesFiltered,
|
|
42298
|
+
onChangeBoardView: changeBoardView,
|
|
42299
|
+
onToggleIdentity: toggleIdentity,
|
|
42300
|
+
onOnlyIdentity: isolateIdentity,
|
|
42301
|
+
onToggleCategoriesExpanded: () => setCategoriesExpanded(!categoriesExpanded),
|
|
42302
|
+
onSetIdentitiesExpanded: setIdentitiesExpanded,
|
|
41560
42303
|
onReturnToLive
|
|
41561
42304
|
},
|
|
41562
42305
|
chart: {
|
|
@@ -41573,9 +42316,12 @@ var TimeScrubber = ({
|
|
|
41573
42316
|
issueBarRange: populatedRange(issueBars),
|
|
41574
42317
|
commitBars,
|
|
41575
42318
|
commitBarRange: populatedRange(commitBars),
|
|
41576
|
-
|
|
41577
|
-
|
|
41578
|
-
|
|
42319
|
+
scatterLayers,
|
|
42320
|
+
issueSeriesColor: soleIdentity?.color ?? boardViewColor(boardView),
|
|
42321
|
+
dragging,
|
|
42322
|
+
// Exits still need their animation, so this only silences a series
|
|
42323
|
+
// that has finished arriving.
|
|
42324
|
+
commits: shown.commits,
|
|
41579
42325
|
hoveredCommitSha: hoveredCommit?.commit.sha ?? null,
|
|
41580
42326
|
hoveredBucketIndex,
|
|
41581
42327
|
hoveredCommitBucketIndex,
|
|
@@ -41615,18 +42361,8 @@ var TimeScrubber = ({
|
|
|
41615
42361
|
setHoveredCommitBucketIndex(bucketIndexFromEvent(event));
|
|
41616
42362
|
},
|
|
41617
42363
|
onCommitTrackMouseLeave: () => setHoveredCommitBucketIndex(null),
|
|
41618
|
-
|
|
41619
|
-
|
|
41620
|
-
count: bucket.count,
|
|
41621
|
-
t: bucket.t,
|
|
41622
|
-
fraction: axis.fractionForTime(bucket.t)
|
|
41623
|
-
}),
|
|
41624
|
-
onIssueDotLeave: () => setHoveredEvent(null),
|
|
41625
|
-
onCommitDotEnter: (commit) => setHoveredCommit({
|
|
41626
|
-
commit,
|
|
41627
|
-
fraction: axis.fractionForTime(commit.time)
|
|
41628
|
-
}),
|
|
41629
|
-
onCommitDotLeave: () => setHoveredCommit(null),
|
|
42364
|
+
onScatterPointEnter,
|
|
42365
|
+
onScatterPointLeave,
|
|
41630
42366
|
onInspectCommit
|
|
41631
42367
|
}
|
|
41632
42368
|
}
|
|
@@ -41717,8 +42453,8 @@ var nodeRef = (id) => id.slice(-NODE_REF_LENGTH).toUpperCase();
|
|
|
41717
42453
|
// source/gui/client/lib/gui-state-helper.tsx
|
|
41718
42454
|
var getResultValue = (payload) => {
|
|
41719
42455
|
if (!payload) return void 0;
|
|
41720
|
-
if (typeof payload === "object" && payload !== null && "value" in payload
|
|
41721
|
-
return payload.value;
|
|
42456
|
+
if (typeof payload === "object" && payload !== null && "value" in payload) {
|
|
42457
|
+
return payload.value ?? void 0;
|
|
41722
42458
|
}
|
|
41723
42459
|
if (typeof payload === "object" && payload !== null && "content" in payload && payload.content?.[0]?.text) {
|
|
41724
42460
|
return JSON.parse(payload.content[0].text).value;
|
|
@@ -41911,6 +42647,7 @@ var blobToBase64 = (blob) => new Promise((resolve, reject) => {
|
|
|
41911
42647
|
|
|
41912
42648
|
// source/gui/client/App.tsx
|
|
41913
42649
|
var import_jsx_runtime33 = __toESM(require_jsx_runtime(), 1);
|
|
42650
|
+
var EMPTY_COMMENTS = {};
|
|
41914
42651
|
var DropIndicator = () => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
41915
42652
|
"div",
|
|
41916
42653
|
{
|
|
@@ -41955,7 +42692,7 @@ var App = () => {
|
|
|
41955
42692
|
const [mutationGate] = (0, import_react16.useState)(createMutationGate);
|
|
41956
42693
|
const selectedTab = searchParams.get("tab") === "comments" ? "comments" : "overview";
|
|
41957
42694
|
const navigate = useNavigate();
|
|
41958
|
-
const selectedBoard = (state && boardId ? findBoard(state, boardId) : null) ?? state?.boards[0] ?? null;
|
|
42695
|
+
const selectedBoard = (state && boardId ? findBoard(state, boardId) : null) ?? state?.boards?.[0] ?? null;
|
|
41959
42696
|
const selectedBoardId = selectedBoard?.id ?? null;
|
|
41960
42697
|
const selectedBoardIdRef = (0, import_react16.useRef)(selectedBoardId);
|
|
41961
42698
|
selectedBoardIdRef.current = selectedBoardId;
|
|
@@ -41980,9 +42717,38 @@ var App = () => {
|
|
|
41980
42717
|
if (!boardSlug) return;
|
|
41981
42718
|
void navigate(`/board/${boardSlug}`);
|
|
41982
42719
|
};
|
|
41983
|
-
const commentsByIssueId = state?.commentsByIssueId ??
|
|
42720
|
+
const commentsByIssueId = state?.commentsByIssueId ?? EMPTY_COMMENTS;
|
|
42721
|
+
const [issueDetail, setIssueDetail] = (0, import_react16.useState)(null);
|
|
42722
|
+
const [boardFilter, setBoardFilter] = (0, import_react16.useState)(null);
|
|
42723
|
+
const { visibleSwimlanes, hiddenIssueCount } = (0, import_react16.useMemo)(() => {
|
|
42724
|
+
const swimlanes = selectedBoard?.swimlanes ?? [];
|
|
42725
|
+
if (!boardFilter) return { visibleSwimlanes: swimlanes, hiddenIssueCount: 0 };
|
|
42726
|
+
let hidden = 0;
|
|
42727
|
+
const visible = swimlanes.map((swimlane) => {
|
|
42728
|
+
const issues = swimlane.issues.filter(
|
|
42729
|
+
(issue) => issuePassesBoardFilter(
|
|
42730
|
+
issue,
|
|
42731
|
+
(commentsByIssueId[issue.id] ?? []).map((comment) => comment.author.id),
|
|
42732
|
+
boardFilter
|
|
42733
|
+
)
|
|
42734
|
+
);
|
|
42735
|
+
hidden += swimlane.issues.length - issues.length;
|
|
42736
|
+
return { ...swimlane, issues };
|
|
42737
|
+
});
|
|
42738
|
+
return { visibleSwimlanes: visible, hiddenIssueCount: hidden };
|
|
42739
|
+
}, [selectedBoard, boardFilter, commentsByIssueId]);
|
|
41984
42740
|
const attachmentsByIssueId = state?.attachmentsByIssueId ?? {};
|
|
41985
42741
|
const [attachmentUploadStatus, setAttachmentUploadStatus] = (0, import_react16.useState)({ state: "idle" });
|
|
42742
|
+
(0, import_react16.useEffect)(() => {
|
|
42743
|
+
if (!selectedIssue) {
|
|
42744
|
+
setIssueDetail(null);
|
|
42745
|
+
return;
|
|
42746
|
+
}
|
|
42747
|
+
sendSocketJson(socketRef.current, {
|
|
42748
|
+
type: "issue:get",
|
|
42749
|
+
payload: { issueId: selectedIssue.id }
|
|
42750
|
+
});
|
|
42751
|
+
}, [selectedIssue?.id, state]);
|
|
41986
42752
|
const requestState = () => {
|
|
41987
42753
|
sendSocketJson(socketRef.current, { type: "state:get" });
|
|
41988
42754
|
};
|
|
@@ -42010,6 +42776,10 @@ var App = () => {
|
|
|
42010
42776
|
const nextState = getResultValue(message.payload);
|
|
42011
42777
|
if (nextState) setState(nextState);
|
|
42012
42778
|
}
|
|
42779
|
+
if (message.type === "issue") {
|
|
42780
|
+
const detail = getResultValue(message.payload);
|
|
42781
|
+
if (detail) setIssueDetail(detail);
|
|
42782
|
+
}
|
|
42013
42783
|
if (message.type === "issue:created") {
|
|
42014
42784
|
const created = getResultValue(message.payload);
|
|
42015
42785
|
if (created && boardId) {
|
|
@@ -42070,8 +42840,9 @@ var App = () => {
|
|
|
42070
42840
|
};
|
|
42071
42841
|
}, [boardId, navigate]);
|
|
42072
42842
|
(0, import_react16.useEffect)(() => {
|
|
42073
|
-
|
|
42074
|
-
|
|
42843
|
+
const first = state?.boards?.[0];
|
|
42844
|
+
if (!boardId && first) {
|
|
42845
|
+
void navigate(`/board/${first.ref}`, { replace: true });
|
|
42075
42846
|
}
|
|
42076
42847
|
}, [boardId, state, navigate]);
|
|
42077
42848
|
(0, import_react16.useEffect)(() => {
|
|
@@ -42468,7 +43239,8 @@ var App = () => {
|
|
|
42468
43239
|
onInspectCommit: inspectCommit,
|
|
42469
43240
|
timeTravel: state?.timeTravel ?? { mode: "live", asOfTime: null },
|
|
42470
43241
|
onScrub: scrubToTime,
|
|
42471
|
-
onReturnToLive: returnToLive
|
|
43242
|
+
onReturnToLive: returnToLive,
|
|
43243
|
+
onBoardFilterChange: setBoardFilter
|
|
42472
43244
|
}
|
|
42473
43245
|
),
|
|
42474
43246
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
@@ -42495,23 +43267,34 @@ var App = () => {
|
|
|
42495
43267
|
overflow: "hidden"
|
|
42496
43268
|
},
|
|
42497
43269
|
children: [
|
|
42498
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
42499
|
-
|
|
43270
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
43271
|
+
"div",
|
|
42500
43272
|
{
|
|
42501
|
-
|
|
42502
|
-
|
|
42503
|
-
|
|
42504
|
-
|
|
42505
|
-
|
|
42506
|
-
}
|
|
42507
|
-
|
|
42508
|
-
|
|
42509
|
-
|
|
42510
|
-
|
|
42511
|
-
|
|
42512
|
-
|
|
43273
|
+
style: {
|
|
43274
|
+
padding: "20px 10px",
|
|
43275
|
+
display: "flex",
|
|
43276
|
+
alignItems: "center",
|
|
43277
|
+
gap: 10
|
|
43278
|
+
},
|
|
43279
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
43280
|
+
Dropdown,
|
|
43281
|
+
{
|
|
43282
|
+
testId: "board-switcher",
|
|
43283
|
+
label: "Board:",
|
|
43284
|
+
value: selectedBoard ? {
|
|
43285
|
+
id: selectedBoard.id,
|
|
43286
|
+
label: selectedBoard.title
|
|
43287
|
+
} : null,
|
|
43288
|
+
items: state?.boards.map((board) => ({
|
|
43289
|
+
id: board.id,
|
|
43290
|
+
label: board.title
|
|
43291
|
+
})) ?? [],
|
|
43292
|
+
placeholder: "Loading...",
|
|
43293
|
+
onSelect: selectBoard
|
|
43294
|
+
}
|
|
43295
|
+
)
|
|
42513
43296
|
}
|
|
42514
|
-
)
|
|
43297
|
+
),
|
|
42515
43298
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
42516
43299
|
"div",
|
|
42517
43300
|
{
|
|
@@ -42524,7 +43307,7 @@ var App = () => {
|
|
|
42524
43307
|
overflowY: "hidden"
|
|
42525
43308
|
},
|
|
42526
43309
|
children: [
|
|
42527
|
-
|
|
43310
|
+
visibleSwimlanes.map((swimlane) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
42528
43311
|
SwimlaneColumn,
|
|
42529
43312
|
{
|
|
42530
43313
|
swimlane,
|
|
@@ -42591,9 +43374,9 @@ var App = () => {
|
|
|
42591
43374
|
IssueDetails,
|
|
42592
43375
|
{
|
|
42593
43376
|
whoAmI: state.user,
|
|
42594
|
-
issue: selectedIssue,
|
|
43377
|
+
issue: issueDetail?.issueId === selectedIssue.id ? { ...selectedIssue, description: issueDetail.description } : selectedIssue,
|
|
42595
43378
|
activeTab: selectedTab,
|
|
42596
|
-
comments:
|
|
43379
|
+
comments: issueDetail?.issueId === selectedIssue.id ? issueDetail.comments : [],
|
|
42597
43380
|
onChangeTab: changeIssueDetailsTab,
|
|
42598
43381
|
onClose: closeIssueDetails,
|
|
42599
43382
|
onEditTitle: editIssueTitle,
|