diff --git a/changelog.md b/changelog.md
index ae5702a..4e7bd56 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,16 @@
## Pending
+- remove redundant card wrappers around Cloudly tables (ui)
+ - Lets `dees-table` provide its own card shell in service, image, and task history views.
+ - Moves the live deployment refresh action into the table header actions.
+
+### Fixes
+
+- remove redundant wrappers around Cloudly tables (ui)
+ - Let dees-table provide its own card shell in service, image, and task history views.
+ - Move the live deployments refresh action into the deployments table header actions.
+ - Bump @types/node to ^25.9.1.
## 2026-05-26 - 6.3.0
diff --git a/package.json b/package.json
index cbaf393..663d0c3 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
"@git.zone/tstest": "^3.6.6",
"@git.zone/tswatch": "^3.3.5",
"@push.rocks/smartnetwork": "^4.7.1",
- "@types/node": "^25.9.0"
+ "@types/node": "^25.9.1"
},
"dependencies": {
"@api.global/typedrequest": "3.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f85066f..a9363c7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -178,8 +178,8 @@ importers:
specifier: ^4.7.1
version: 4.7.1
'@types/node':
- specifier: ^25.9.0
- version: 25.9.0
+ specifier: ^25.9.1
+ version: 25.9.1
packages:
@@ -2308,11 +2308,11 @@ packages:
'@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
- '@types/node@22.19.18':
- resolution: {integrity: sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==}
+ '@types/node@22.19.19':
+ resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==}
- '@types/node@25.9.0':
- resolution: {integrity: sha512-AOQwYUNolgy3VosiRqXrACUXTN8nJUtPl7FJXMqZVyxiiCLhQuG3jXKvCS1ALr+Y2OmZhzzLVlYPEqJaiqkaJQ==}
+ '@types/node@25.9.1':
+ resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
'@types/randomatic@3.1.5':
resolution: {integrity: sha512-VCwCTw6qh1pRRw+5rNTAwqPmf6A+hdrkdM7dBpZVmhl7g+em3ONXlYK/bWPVKqVGMWgP0d1bog8Vc/X6zRwRRQ==}
@@ -5735,7 +5735,7 @@ snapshots:
'@happy-dom/global-registrator@20.9.0':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
happy-dom: 20.9.0
transitivePeerDependencies:
- bufferutil
@@ -5855,7 +5855,7 @@ snapshots:
'@inquirer/figures': 1.0.15
'@inquirer/type': 2.0.0
'@types/mute-stream': 0.0.4
- '@types/node': 22.19.18
+ '@types/node': 22.19.19
'@types/wrap-ansi': 3.0.0
ansi-escapes: 4.3.2
cli-width: 4.1.0
@@ -7915,7 +7915,7 @@ snapshots:
'@types/clean-css@4.2.11':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
source-map: 0.6.1
'@types/debug@4.1.13':
@@ -7931,7 +7931,7 @@ snapshots:
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/hast@3.0.4':
dependencies:
@@ -7947,12 +7947,12 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/linkify-it@5.0.0': {}
@@ -7973,16 +7973,16 @@ snapshots:
'@types/mute-stream@0.0.4':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/node-fetch@2.6.13':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
form-data: 4.0.5
'@types/node-forge@1.3.14':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/node@16.9.1': {}
@@ -7990,11 +7990,11 @@ snapshots:
dependencies:
undici-types: 5.26.5
- '@types/node@22.19.18':
+ '@types/node@22.19.19':
dependencies:
undici-types: 6.21.0
- '@types/node@25.9.0':
+ '@types/node@25.9.1':
dependencies:
undici-types: 7.24.6
@@ -8012,7 +8012,7 @@ snapshots:
'@types/through2@2.0.41':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/trusted-types@2.0.7': {}
@@ -8040,11 +8040,11 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/yauzl@2.10.3':
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
optional: true
'@ungap/structured-clone@1.3.1': {}
@@ -8780,7 +8780,7 @@ snapshots:
happy-dom@20.9.0:
dependencies:
- '@types/node': 25.9.0
+ '@types/node': 25.9.1
'@types/whatwg-mimetype': 3.0.2
'@types/ws': 8.18.1
entities: 7.0.1
diff --git a/ts_web/elements/views/images/index.ts b/ts_web/elements/views/images/index.ts
index 1d62ed7..187114e 100644
--- a/ts_web/elements/views/images/index.ts
+++ b/ts_web/elements/views/images/index.ts
@@ -42,8 +42,8 @@ export class CloudlyViewImages extends DeesElement {
.detail-title { margin: 0; font-size: 26px; font-weight: 700; color: var(--ci-shade-7, #e4e4e7); }
.detail-subtitle { margin-top: 6px; color: var(--ci-shade-4, #71717a); font-size: 14px; overflow-wrap: anywhere; }
.back-button { border: 1px solid var(--ci-shade-2, #27272a); border-radius: 7px; padding: 9px 13px; font-size: 13px; cursor: pointer; background: var(--ci-shade-1, #09090b); color: var(--ci-shade-7, #e4e4e7); }
- .summary-card, .detail-card { background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 9px; padding: 16px; }
- .spaced-card { margin-top: 14px; }
+ .detail-card { background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 9px; padding: 16px; }
+ .spaced-table, .spaced-card { margin-top: 14px; }
.details-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.section-title { font-size: 14px; font-weight: 700; color: var(--ci-shade-7, #e4e4e7); margin-bottom: 10px; }
.kv-list { display: grid; gap: 8px; }
@@ -172,43 +172,43 @@ export class CloudlyViewImages extends DeesElement {
-
-
Versions
+
({
+ Version: versionArg.versionString,
+ Source: this.renderSourceBadge(versionArg.source),
+ Size: this.formatBytes(versionArg.size),
+ Digest: versionArg.digest || '-',
+ Repository: versionArg.registryRepository || '-',
+ Tag: versionArg.registryTag || '-',
+ Storage: versionArg.storagePath || '-',
+ Created: this.formatDate(versionArg.createdAt),
+ })}
+ >
+
+ ${servicesUsingImage.length ? html`
({
- Version: versionArg.versionString,
- Source: this.renderSourceBadge(versionArg.source),
- Size: this.formatBytes(versionArg.size),
- Digest: versionArg.digest || '-',
- Repository: versionArg.registryRepository || '-',
- Tag: versionArg.registryTag || '-',
- Storage: versionArg.storagePath || '-',
- Created: this.formatDate(versionArg.createdAt),
+ class="spaced-table"
+ .heading1=${'Service Usage'}
+ .heading2=${'Services currently configured with this image ID'}
+ .data=${servicesUsingImage}
+ .displayFunction=${(serviceArg: plugins.interfaces.data.IService) => ({
+ Name: serviceArg.data.name,
+ Version: serviceArg.data.imageVersion || '-',
+ Category: serviceArg.data.serviceCategory || 'workload',
+ Strategy: serviceArg.data.deploymentStrategy || 'custom',
+ Domains: serviceArg.data.domains?.map((domainArg) => domainArg.name).join(', ') || '-',
+ Deployments: serviceArg.data.deploymentIds?.length || 0,
})}
>
-
-
-
-
Services Using This Image
- ${servicesUsingImage.length ? html`
-
({
- Name: serviceArg.data.name,
- Version: serviceArg.data.imageVersion || '-',
- Category: serviceArg.data.serviceCategory || 'workload',
- Strategy: serviceArg.data.deploymentStrategy || 'custom',
- Domains: serviceArg.data.domains?.map((domainArg) => domainArg.name).join(', ') || '-',
- Deployments: serviceArg.data.deploymentIds?.length || 0,
- })}
- >
- ` : html`
No services currently reference this image.
`}
-
+ ` : html`
+
+
Services Using This Image
+
No services currently reference this image.
+
+ `}
diff --git a/ts_web/elements/views/services/index.ts b/ts_web/elements/views/services/index.ts
index 68c1d76..0ac42d8 100644
--- a/ts_web/elements/views/services/index.ts
+++ b/ts_web/elements/views/services/index.ts
@@ -368,66 +368,65 @@ export class CloudlyViewServices extends DeesElement {
-
-
-
-
Deployments
-
Container-level runtime actions happen here.
-
-
-
- ${this.deploymentsLoading ? html`
Loading deployments...
` : html`
-
({
- Status: this.renderStatusBadge(deploymentArg.status),
- Node: deploymentArg.nodeName || deploymentArg.nodeId || '-',
- Slot: deploymentArg.slot || '-',
- Version: deploymentArg.version || service.data.imageVersion,
- Container: deploymentArg.containerId ? deploymentArg.containerId.slice(0, 12) : '-',
- CPU: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.cpuUsagePercent.toFixed(1)}%` : '-',
- Memory: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.memoryUsedMB} MB` : '-',
- Updated: deploymentArg.updatedAt ? new Date(deploymentArg.updatedAt).toLocaleString() : '-',
- })}
- .dataActions=${[
- {
- name: 'Details',
- iconName: 'lucide:Eye',
- type: ['contextmenu', 'inRow', 'doubleClick'],
- actionFunc: async (actionDataArg: any) => {
- await this.showDeploymentDetailsModal(actionDataArg.item);
- },
+ ${this.deploymentsLoading ? html`Loading deployments...
` : html`
+ ({
+ Status: this.renderStatusBadge(deploymentArg.status),
+ Node: deploymentArg.nodeName || deploymentArg.nodeId || '-',
+ Slot: deploymentArg.slot || '-',
+ Version: deploymentArg.version || service.data.imageVersion,
+ Container: deploymentArg.containerId ? deploymentArg.containerId.slice(0, 12) : '-',
+ CPU: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.cpuUsagePercent.toFixed(1)}%` : '-',
+ Memory: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.memoryUsedMB} MB` : '-',
+ Updated: deploymentArg.updatedAt ? new Date(deploymentArg.updatedAt).toLocaleString() : '-',
+ })}
+ .dataActions=${[
+ {
+ name: 'Refresh',
+ iconName: 'refresh-cw',
+ type: ['header'],
+ actionFunc: async () => {
+ await this.loadDeploymentsForService(service);
},
- {
- name: 'Open IDE',
- iconName: 'terminal',
- type: ['contextmenu', 'inRow'],
- actionFunc: async (actionDataArg: any) => {
- await this.openDeploymentWorkspace(actionDataArg.item);
- },
+ },
+ {
+ name: 'Details',
+ iconName: 'lucide:Eye',
+ type: ['contextmenu', 'inRow', 'doubleClick'],
+ actionFunc: async (actionDataArg: any) => {
+ await this.showDeploymentDetailsModal(actionDataArg.item);
},
- {
- name: 'Restart',
- iconName: 'refresh-cw',
- type: ['contextmenu', 'inRow'],
- actionFunc: async (actionDataArg: any) => {
- await this.restartDeployment(actionDataArg.item);
- },
+ },
+ {
+ name: 'Open IDE',
+ iconName: 'terminal',
+ type: ['contextmenu', 'inRow'],
+ actionFunc: async (actionDataArg: any) => {
+ await this.openDeploymentWorkspace(actionDataArg.item);
},
- {
- name: 'Kill Container',
- iconName: 'skull',
- type: ['contextmenu', 'inRow'],
- actionFunc: async (actionDataArg: any) => {
- await this.confirmKillDeployment(actionDataArg.item);
- },
+ },
+ {
+ name: 'Restart',
+ iconName: 'refresh-cw',
+ type: ['contextmenu', 'inRow'],
+ actionFunc: async (actionDataArg: any) => {
+ await this.restartDeployment(actionDataArg.item);
},
- ] as plugins.deesCatalog.ITableAction[]}
- >
- `}
-
+ },
+ {
+ name: 'Kill Container',
+ iconName: 'skull',
+ type: ['contextmenu', 'inRow'],
+ actionFunc: async (actionDataArg: any) => {
+ await this.confirmKillDeployment(actionDataArg.item);
+ },
+ },
+ ] as plugins.deesCatalog.ITableAction[]}
+ >
+ `}
diff --git a/ts_web/elements/views/tasks/index.ts b/ts_web/elements/views/tasks/index.ts
index 3b6dc0f..cb71918 100644
--- a/ts_web/elements/views/tasks/index.ts
+++ b/ts_web/elements/views/tasks/index.ts
@@ -266,32 +266,30 @@ export class CloudlyViewTasks extends DeesElement {
Execution History
-
- {
- return {
- Task: itemArg.data.taskName,
- Status: html`${itemArg.data.status}`,
- 'Started At': formatDate(itemArg.data.startedAt),
- Duration: itemArg.data.duration ? formatDuration(itemArg.data.duration) : '-',
- 'Triggered By': itemArg.data.triggeredBy,
- Logs: itemArg.data.logs?.length || 0,
- } as any;
- }}
- .actionFunction=${async (itemArg: plugins.interfaces.data.ITaskExecution) => {
- const actions: any[] = [
- { name: 'View Details', iconName: 'lucide:Eye', type: ['inRow'], actionFunc: async () => { this.selectedExecution = itemArg; } }
- ];
- if (itemArg.data.status === 'running') {
- actions.push({ name: 'Cancel', iconName: 'lucide:SquareX', type: ['inRow'], actionFunc: async () => { await appstate.dataState.dispatchAction(appstate.taskActions.cancelTask, { executionId: itemArg.id }); await this.loadExecutionsWithFilter(); } });
- }
- return actions;
- }}
- >
-
+ {
+ return {
+ Task: itemArg.data.taskName,
+ Status: html`${itemArg.data.status}`,
+ 'Started At': formatDate(itemArg.data.startedAt),
+ Duration: itemArg.data.duration ? formatDuration(itemArg.data.duration) : '-',
+ 'Triggered By': itemArg.data.triggeredBy,
+ Logs: itemArg.data.logs?.length || 0,
+ } as any;
+ }}
+ .actionFunction=${async (itemArg: plugins.interfaces.data.ITaskExecution) => {
+ const actions: any[] = [
+ { name: 'View Details', iconName: 'lucide:Eye', type: ['inRow'], actionFunc: async () => { this.selectedExecution = itemArg; } }
+ ];
+ if (itemArg.data.status === 'running') {
+ actions.push({ name: 'Cancel', iconName: 'lucide:SquareX', type: ['inRow'], actionFunc: async () => { await appstate.dataState.dispatchAction(appstate.taskActions.cancelTask, { executionId: itemArg.id }); await this.loadExecutionsWithFilter(); } });
+ }
+ return actions;
+ }}
+ >
${this.selectedExecution ? html`
Execution Details