feat(web): add database overview panel, collection overview and resizable panels; show/hide system databases; use code editor with change-tracking in document view; add getDatabaseStats API and typings; enable overwrite for S3 uploads

This commit is contained in:
2026-01-25 17:34:52 +00:00
parent 2ca5f52da3
commit a26e7a5a20
17 changed files with 718 additions and 143 deletions

View File

@@ -90,6 +90,33 @@ export class TsviewMongoCollections extends DeesElement {
font-size: 12px;
font-style: italic;
}
.overview-item {
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
transition: background 0.1s;
color: #a5d6a7;
margin-bottom: 4px;
}
.overview-item:hover {
background: rgba(255, 255, 255, 0.05);
}
.overview-item.selected {
background: rgba(165, 214, 167, 0.15);
color: #a5d6a7;
}
.overview-item svg {
width: 14px;
height: 14px;
}
`,
];
@@ -168,36 +195,56 @@ export class TsviewMongoCollections extends DeesElement {
await this.loadCollections();
}
private selectOverview() {
this.dispatchEvent(
new CustomEvent('collection-selected', {
detail: '__overview__',
bubbles: true,
composed: true,
})
);
}
render() {
if (this.loading) {
return html`<div class="loading-state">Loading collections...</div>`;
}
if (this.collections.length === 0) {
return html`<div class="empty-state">No collections</div>`;
}
return html`
<div class="collections-list">
${this.collections.map(
(coll) => html`
<div
class="collection-item ${this.selectedCollection === coll.name ? 'selected' : ''}"
@click=${() => this.selectCollection(coll.name)}
@contextmenu=${(e: MouseEvent) => this.handleCollectionContextMenu(e, coll)}
>
<span class="collection-name">
<svg class="collection-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
</svg>
${coll.name}
</span>
${coll.count !== undefined
? html`<span class="collection-count">${formatCount(coll.count)}</span>`
: ''}
</div>
`
)}
<div
class="overview-item ${this.selectedCollection === '__overview__' ? 'selected' : ''}"
@click=${() => this.selectOverview()}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
Overview
</div>
${this.collections.length === 0
? html`<div class="empty-state">No collections</div>`
: this.collections.map(
(coll) => html`
<div
class="collection-item ${this.selectedCollection === coll.name ? 'selected' : ''}"
@click=${() => this.selectCollection(coll.name)}
@contextmenu=${(e: MouseEvent) => this.handleCollectionContextMenu(e, coll)}
>
<span class="collection-name">
<svg class="collection-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
</svg>
${coll.name}
</span>
${coll.count !== undefined
? html`<span class="collection-count">${formatCount(coll.count)}</span>`
: ''}
</div>
`
)}
</div>
`;
}