mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-28 08:52:45 +00:00
refactor(ui): dedupe usage session rows
This commit is contained in:
@@ -500,35 +500,39 @@ function renderObject(params: {
|
|||||||
const additional = schema.additionalProperties;
|
const additional = schema.additionalProperties;
|
||||||
const allowExtra = Boolean(additional) && typeof additional === "object";
|
const allowExtra = Boolean(additional) && typeof additional === "object";
|
||||||
|
|
||||||
|
const fields = html`
|
||||||
|
${sorted.map(([propKey, node]) =>
|
||||||
|
renderNode({
|
||||||
|
schema: node,
|
||||||
|
value: obj[propKey],
|
||||||
|
path: [...path, propKey],
|
||||||
|
hints,
|
||||||
|
unsupported,
|
||||||
|
disabled,
|
||||||
|
onPatch,
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
${
|
||||||
|
allowExtra
|
||||||
|
? renderMapField({
|
||||||
|
schema: additional,
|
||||||
|
value: obj,
|
||||||
|
path,
|
||||||
|
hints,
|
||||||
|
unsupported,
|
||||||
|
disabled,
|
||||||
|
reservedKeys: reserved,
|
||||||
|
onPatch,
|
||||||
|
})
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
// For top-level, don't wrap in collapsible
|
// For top-level, don't wrap in collapsible
|
||||||
if (path.length === 1) {
|
if (path.length === 1) {
|
||||||
return html`
|
return html`
|
||||||
<div class="cfg-fields">
|
<div class="cfg-fields">
|
||||||
${sorted.map(([propKey, node]) =>
|
${fields}
|
||||||
renderNode({
|
|
||||||
schema: node,
|
|
||||||
value: obj[propKey],
|
|
||||||
path: [...path, propKey],
|
|
||||||
hints,
|
|
||||||
unsupported,
|
|
||||||
disabled,
|
|
||||||
onPatch,
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
${
|
|
||||||
allowExtra
|
|
||||||
? renderMapField({
|
|
||||||
schema: additional,
|
|
||||||
value: obj,
|
|
||||||
path,
|
|
||||||
hints,
|
|
||||||
unsupported,
|
|
||||||
disabled,
|
|
||||||
reservedKeys: reserved,
|
|
||||||
onPatch,
|
|
||||||
})
|
|
||||||
: nothing
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -542,31 +546,7 @@ function renderObject(params: {
|
|||||||
</summary>
|
</summary>
|
||||||
${help ? html`<div class="cfg-object__help">${help}</div>` : nothing}
|
${help ? html`<div class="cfg-object__help">${help}</div>` : nothing}
|
||||||
<div class="cfg-object__content">
|
<div class="cfg-object__content">
|
||||||
${sorted.map(([propKey, node]) =>
|
${fields}
|
||||||
renderNode({
|
|
||||||
schema: node,
|
|
||||||
value: obj[propKey],
|
|
||||||
path: [...path, propKey],
|
|
||||||
hints,
|
|
||||||
unsupported,
|
|
||||||
disabled,
|
|
||||||
onPatch,
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
${
|
|
||||||
allowExtra
|
|
||||||
? renderMapField({
|
|
||||||
schema: additional,
|
|
||||||
value: obj,
|
|
||||||
path,
|
|
||||||
hints,
|
|
||||||
unsupported,
|
|
||||||
disabled,
|
|
||||||
reservedKeys: reserved,
|
|
||||||
onPatch,
|
|
||||||
})
|
|
||||||
: nothing
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -649,6 +649,38 @@ function renderSessionsCard(
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const renderSessionBarRow = (s: UsageSessionEntry, isSelected: boolean) => {
|
||||||
|
const value = getSessionValue(s);
|
||||||
|
const displayLabel = formatSessionListLabel(s);
|
||||||
|
const meta = buildSessionMeta(s);
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="session-bar-row ${isSelected ? "selected" : ""}"
|
||||||
|
@click=${(e: MouseEvent) => onSelectSession(s.key, e.shiftKey)}
|
||||||
|
title="${s.key}"
|
||||||
|
>
|
||||||
|
<div class="session-bar-label">
|
||||||
|
<div class="session-bar-title">${displayLabel}</div>
|
||||||
|
${meta.length > 0 ? html`<div class="session-bar-meta">${meta.join(" · ")}</div>` : nothing}
|
||||||
|
</div>
|
||||||
|
<div class="session-bar-track" style="display: none;"></div>
|
||||||
|
<div class="session-bar-actions">
|
||||||
|
<button
|
||||||
|
class="session-copy-btn"
|
||||||
|
title="Copy session name"
|
||||||
|
@click=${(e: MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
void copySessionName(s);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
<div class="session-bar-value">${isTokenMode ? formatTokens(value) : formatCost(value)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
const selectedSet = new Set(selectedSessions);
|
const selectedSet = new Set(selectedSessions);
|
||||||
const selectedEntries = sortedWithDir.filter((s) => selectedSet.has(s.key));
|
const selectedEntries = sortedWithDir.filter((s) => selectedSet.has(s.key));
|
||||||
const selectedCount = selectedEntries.length;
|
const selectedCount = selectedEntries.length;
|
||||||
@@ -720,83 +752,22 @@ function renderSessionsCard(
|
|||||||
<div class="muted" style="padding: 20px; text-align: center">No recent sessions</div>
|
<div class="muted" style="padding: 20px; text-align: center">No recent sessions</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="session-bars" style="max-height: 220px; margin-top: 6px;">
|
<div class="session-bars" style="max-height: 220px; margin-top: 6px;">
|
||||||
${recentEntries.map((s) => {
|
${recentEntries.map((s) => renderSessionBarRow(s, selectedSet.has(s.key)))}
|
||||||
const value = getSessionValue(s);
|
</div>
|
||||||
const isSelected = selectedSet.has(s.key);
|
`
|
||||||
const displayLabel = formatSessionListLabel(s);
|
|
||||||
const meta = buildSessionMeta(s);
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class="session-bar-row ${isSelected ? "selected" : ""}"
|
|
||||||
@click=${(e: MouseEvent) => onSelectSession(s.key, e.shiftKey)}
|
|
||||||
title="${s.key}"
|
|
||||||
>
|
|
||||||
<div class="session-bar-label">
|
|
||||||
<div class="session-bar-title">${displayLabel}</div>
|
|
||||||
${meta.length > 0 ? html`<div class="session-bar-meta">${meta.join(" · ")}</div>` : nothing}
|
|
||||||
</div>
|
|
||||||
<div class="session-bar-track" style="display: none;"></div>
|
|
||||||
<div class="session-bar-actions">
|
|
||||||
<button
|
|
||||||
class="session-copy-btn"
|
|
||||||
title="Copy session name"
|
|
||||||
@click=${(e: MouseEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
void copySessionName(s);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy
|
|
||||||
</button>
|
|
||||||
<div class="session-bar-value">${isTokenMode ? formatTokens(value) : formatCost(value)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: sessions.length === 0
|
: sessions.length === 0
|
||||||
? html`
|
? html`
|
||||||
<div class="muted" style="padding: 20px; text-align: center">No sessions in range</div>
|
<div class="muted" style="padding: 20px; text-align: center">No sessions in range</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="session-bars">
|
<div class="session-bars">
|
||||||
${sortedWithDir.slice(0, 50).map((s) => {
|
${sortedWithDir
|
||||||
const value = getSessionValue(s);
|
.slice(0, 50)
|
||||||
const isSelected = selectedSessions.includes(s.key);
|
.map((s) => renderSessionBarRow(s, selectedSet.has(s.key)))}
|
||||||
const displayLabel = formatSessionListLabel(s);
|
${sessions.length > 50 ? html`<div class="muted" style="padding: 8px; text-align: center; font-size: 11px;">+${sessions.length - 50} more</div>` : nothing}
|
||||||
const meta = buildSessionMeta(s);
|
</div>
|
||||||
|
`
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class="session-bar-row ${isSelected ? "selected" : ""}"
|
|
||||||
@click=${(e: MouseEvent) => onSelectSession(s.key, e.shiftKey)}
|
|
||||||
title="${s.key}"
|
|
||||||
>
|
|
||||||
<div class="session-bar-label">
|
|
||||||
<div class="session-bar-title">${displayLabel}</div>
|
|
||||||
${meta.length > 0 ? html`<div class="session-bar-meta">${meta.join(" · ")}</div>` : nothing}
|
|
||||||
</div>
|
|
||||||
<div class="session-bar-track" style="display: none;"></div>
|
|
||||||
<div class="session-bar-actions">
|
|
||||||
<button
|
|
||||||
class="session-copy-btn"
|
|
||||||
title="Copy session name"
|
|
||||||
@click=${(e: MouseEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
void copySessionName(s);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy
|
|
||||||
</button>
|
|
||||||
<div class="session-bar-value">${isTokenMode ? formatTokens(value) : formatCost(value)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
${sessions.length > 50 ? html`<div class="muted" style="padding: 8px; text-align: center; font-size: 11px;">+${sessions.length - 50} more</div>` : nothing}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
selectedCount > 1
|
selectedCount > 1
|
||||||
@@ -804,37 +775,7 @@ function renderSessionsCard(
|
|||||||
<div style="margin-top: 10px;">
|
<div style="margin-top: 10px;">
|
||||||
<div class="sessions-card-count">Selected (${selectedCount})</div>
|
<div class="sessions-card-count">Selected (${selectedCount})</div>
|
||||||
<div class="session-bars" style="max-height: 160px; margin-top: 6px;">
|
<div class="session-bars" style="max-height: 160px; margin-top: 6px;">
|
||||||
${selectedEntries.map((s) => {
|
${selectedEntries.map((s) => renderSessionBarRow(s, true))}
|
||||||
const value = getSessionValue(s);
|
|
||||||
const displayLabel = formatSessionListLabel(s);
|
|
||||||
const meta = buildSessionMeta(s);
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class="session-bar-row selected"
|
|
||||||
@click=${(e: MouseEvent) => onSelectSession(s.key, e.shiftKey)}
|
|
||||||
title="${s.key}"
|
|
||||||
>
|
|
||||||
<div class="session-bar-label">
|
|
||||||
<div class="session-bar-title">${displayLabel}</div>
|
|
||||||
${meta.length > 0 ? html`<div class="session-bar-meta">${meta.join(" · ")}</div>` : nothing}
|
|
||||||
</div>
|
|
||||||
<div class="session-bar-track" style="display: none;"></div>
|
|
||||||
<div class="session-bar-actions">
|
|
||||||
<button
|
|
||||||
class="session-copy-btn"
|
|
||||||
title="Copy session name"
|
|
||||||
@click=${(e: MouseEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
void copySessionName(s);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy
|
|
||||||
</button>
|
|
||||||
<div class="session-bar-value">${isTokenMode ? formatTokens(value) : formatCost(value)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|||||||
Reference in New Issue
Block a user