Compare commits

...

6 Commits

Author SHA1 Message Date
790b468188 7.8.17
Some checks failed
Default (tags) / security (push) Failing after 40s
Default (tags) / test (push) Failing after 13s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-05 09:26:29 +00:00
24d6d6d2e7 fix(typedserver): Update WebSocket peer tag and add error handling for pushTime 2025-12-05 09:26:27 +00:00
a86fd6c1f3 7.8.16
Some checks failed
Default (tags) / security (push) Failing after 43s
Default (tags) / test (push) Failing after 13s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-05 00:18:07 +00:00
d04179ccbe feat(sw-dash): Add right-click context menu for request cards
- Add dees-catalog dependency for DeesContextmenu component
- Right-click on message card shows context menu with options:
  - Copy Full Message (request + response with all data)
  - Copy Request Payload
  - Copy Response Payload
  - Copy Correlation ID
  - Copy Method Name
  - Filter by Method
  - Show Payload (opens modal)
2025-12-05 00:18:07 +00:00
d6eacf5fcc 7.8.15
Some checks failed
Default (tags) / security (push) Failing after 42s
Default (tags) / test (push) Failing after 14s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-05 00:14:55 +00:00
0f974701d4 feat(sw-dash): Click method tile to filter by that method 2025-12-05 00:14:55 +00:00
5 changed files with 968 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@api.global/typedserver",
"version": "7.8.14",
"version": "7.8.17",
"description": "A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.",
"type": "module",
"exports": {
@@ -62,6 +62,7 @@
"@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedsocket": "^4.1.0",
"@cloudflare/workers-types": "^4.20251202.0",
"@design.estate/dees-catalog": "^2.0.3",
"@design.estate/dees-comms": "^1.0.30",
"@push.rocks/lik": "^6.2.2",
"@push.rocks/smartdelay": "^3.0.5",

848
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -258,7 +258,7 @@ export class TypedServer {
websocket: {
typedRouter: this.typedrouter,
onConnectionOpen: (peer) => {
peer.tags.add('typedserver_frontend');
peer.tags.add('allClients');
console.log(`WebSocket connected: ${peer.id}`);
},
onConnectionClose: (peer) => {
@@ -644,6 +644,8 @@ export class TypedServer {
);
pushTime.fire({
time: this.lastReload,
}).catch(err => {
console.warn('Failed to push latest server change time to client:', err);
});
}
} catch (error) {

View File

@@ -6,6 +6,9 @@ import { customElement, property, state } from 'lit/decorators.js';
// DeesComms for push communication
import * as deesComms from '@design.estate/dees-comms';
// Dees-catalog for UI components
import { DeesContextmenu } from '@design.estate/dees-catalog';
export {
LitElement,
html,
@@ -14,6 +17,7 @@ export {
property,
state,
deesComms,
DeesContextmenu,
};
export type { CSSResult, TemplateResult };

View File

@@ -1,4 +1,4 @@
import { LitElement, html, css, property, state, customElement } from './plugins.js';
import { LitElement, html, css, property, state, customElement, DeesContextmenu } from './plugins.js';
import type { CSSResult, TemplateResult } from './plugins.js';
import { sharedStyles, panelStyles, tableStyles, buttonStyles } from './sw-dash-styles.js';
@@ -237,6 +237,17 @@ export class SwDashRequests extends LitElement {
background: var(--bg-tertiary);
border-radius: var(--radius-sm);
padding: var(--space-2);
cursor: pointer;
transition: background 0.15s ease;
}
.method-stat-card:hover {
background: var(--bg-secondary);
}
.method-stat-card.active {
background: rgba(99, 102, 241, 0.15);
border: 1px solid var(--accent-primary);
}
.method-stat-name {
@@ -504,6 +515,11 @@ export class SwDashRequests extends LitElement {
// Local filtering - no HTTP request
}
private setMethodFilter(method: string): void {
// Toggle: clicking the same method clears the filter
this.methodFilter = this.methodFilter === method ? '' : method;
}
private handleSearch(e: Event): void {
this.searchText = (e.target as HTMLInputElement).value.toLowerCase();
}
@@ -546,6 +562,90 @@ export class SwDashRequests extends LitElement {
this.modalOpen = true;
}
private handleContextMenu(event: MouseEvent, group: IGroupedRequest): void {
// Build full message object for copying
const fullMessage = {
correlationId: group.correlationId,
method: group.method,
timestamp: group.timestamp,
durationMs: group.durationMs,
request: group.request ? {
direction: group.request.direction,
phase: group.request.phase,
timestamp: group.request.timestamp,
payload: group.request.payload,
} : null,
response: group.response ? {
direction: group.response.direction,
phase: group.response.phase,
timestamp: group.response.timestamp,
durationMs: group.response.durationMs,
payload: group.response.payload,
error: group.response.error,
} : null,
};
DeesContextmenu.openContextMenuWithOptions(event, [
{
name: 'Copy Full Message',
iconName: 'copy',
action: async () => {
await navigator.clipboard.writeText(JSON.stringify(fullMessage, null, 2));
},
},
{
name: 'Copy Request Payload',
iconName: 'upload',
disabled: !group.request,
action: async () => {
if (group.request) {
await navigator.clipboard.writeText(JSON.stringify(group.request.payload, null, 2));
}
},
},
{
name: 'Copy Response Payload',
iconName: 'download',
disabled: !group.response,
action: async () => {
if (group.response) {
await navigator.clipboard.writeText(JSON.stringify(group.response.payload, null, 2));
}
},
},
{ divider: true },
{
name: 'Copy Correlation ID',
iconName: 'hash',
action: async () => {
await navigator.clipboard.writeText(group.correlationId);
},
},
{
name: 'Copy Method Name',
iconName: 'tag',
action: async () => {
await navigator.clipboard.writeText(group.method);
},
},
{ divider: true },
{
name: 'Filter by Method',
iconName: 'filter',
action: async () => {
this.setMethodFilter(group.method);
},
},
{
name: 'Show Payload',
iconName: 'eye',
action: async () => {
this.openPayloadModal(group);
},
},
]);
}
private closeModal(): void {
this.modalOpen = false;
this.selectedGroup = null;
@@ -781,7 +881,10 @@ export class SwDashRequests extends LitElement {
<div class="method-stats-title">Methods</div>
<div class="method-stats-grid">
${Object.entries(this.stats.methodCounts).slice(0, 8).map(([method, data]) => html`
<div class="method-stat-card">
<div
class="method-stat-card ${this.methodFilter === method ? 'active' : ''}"
@click="${() => this.setMethodFilter(method)}"
>
<div class="method-stat-name" title="${method}">${method}</div>
<div class="method-stat-details">
<span>${data.requests} req</span>
@@ -813,9 +916,9 @@ export class SwDashRequests extends LitElement {
</select>
<span class="filter-label">Method:</span>
<select class="filter-select" @change="${this.handleMethodFilterChange}">
<select class="filter-select" .value="${this.methodFilter}" @change="${this.handleMethodFilterChange}">
<option value="">All Methods</option>
${this.methods.map(m => html`<option value="${m}">${m}</option>`)}
${this.methods.map(m => html`<option value="${m}" ?selected="${this.methodFilter === m}">${m}</option>`)}
</select>
<input
@@ -838,7 +941,10 @@ export class SwDashRequests extends LitElement {
` : html`
<div class="requests-list">
${groupedLogs.map(group => html`
<div class="request-card ${group.hasError ? 'has-error' : ''}">
<div
class="request-card ${group.hasError ? 'has-error' : ''}"
@contextmenu="${(e: MouseEvent) => this.handleContextMenu(e, group)}"
>
<div class="request-header">
<div>
<div class="request-badges">