Compare commits

...

13 Commits

Author SHA1 Message Date
bee1cafdb4 v3.54.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-04 08:13:56 +00:00
42b40da67c feat(media): rename media tile components to thumbnail components and add shared thumbnail base exports 2026-04-04 08:13:56 +00:00
10cd1e2755 v3.53.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-03 20:27:50 +00:00
68ed024aaa feat(dees-pdf-viewer): add configurable sidebar position support 2026-04-03 20:27:50 +00:00
6b6ccd0e3c v3.52.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-03 19:58:51 +00:00
d933c47b49 fix(dees-pdf-viewer): add top scroll offset when navigating to a page in the PDF viewer 2026-04-03 19:58:51 +00:00
3defbba5fd feat(pdf-viewer): enhance PDF viewer with file size display and footer layout 2026-04-03 19:50:46 +00:00
02522c9a15 v3.52.4
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-03 19:25:18 +00:00
4370efe6fb fix(appui-maincontent): adjust main content background theme colors 2026-04-03 19:25:18 +00:00
cde2a833ef v3.52.3
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-03 13:51:03 +00:00
31fbe22f55 fix(input-richtext): resolve rich text editor initialization and layout issues by bundling Tiptap locally and anchoring editor containers 2026-04-03 13:51:03 +00:00
e6f501e804 v3.52.2
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-03 13:33:56 +00:00
f052fb9c9f fix(chart-log, simple-appdash): align terminal and dashboard theming with brightness mode and improve app dashboard scroll presentation 2026-04-03 13:33:56 +00:00
40 changed files with 431 additions and 206 deletions

View File

@@ -1,5 +1,44 @@
# Changelog
## 2026-04-04 - 3.54.0 - feat(media)
rename media tile components to thumbnail components and add shared thumbnail base exports
- Replaces dees-tile-* media component exports and implementations with dees-thumbnail-* counterparts for audio, image, video, note, folder, and pdf previews.
- Introduces a shared DeesThumbnailBase and shared thumbnail styles for consistent sizing, hover overlays, loading states, error states, and lazy-loading behavior.
- Updates the media index to export the new thumbnail component modules.
- Includes a small layout cleanup in dees-dataview-codebox by removing forced full-height and absolute grid positioning.
## 2026-04-03 - 3.53.0 - feat(dees-pdf-viewer)
add configurable sidebar position support
- introduces a sidebarPosition property with left and right options
- updates viewer layout and footer alignment when the sidebar is displayed on the right
## 2026-04-03 - 3.52.5 - fix(dees-pdf-viewer)
add top scroll offset when navigating to a page in the PDF viewer
- Subtracts 16px from the calculated scroll target so the selected page is not flush against the top edge of the viewer.
- Improves page navigation positioning in the dees-pdf-viewer component.
## 2026-04-03 - 3.52.4 - fix(appui-maincontent)
adjust main content background theme colors
- Update the main content background from pure white/near-black to softer light and dark theme values.
## 2026-04-03 - 3.52.3 - fix(input-richtext)
resolve rich text editor initialization and layout issues by bundling Tiptap locally and anchoring editor containers
- Switch Tiptap imports from CDN URLs to bundled npm packages to avoid duplicate ProseMirror instances
- Update rich text, code, dataview, and terminal preview containers to use absolute inset positioning for stable full-size layouts
- Trigger a component update after rich text editor initialization and improve ProseMirror wrapping behavior
## 2026-04-03 - 3.52.2 - fix(chart-log, simple-appdash)
align terminal and dashboard theming with brightness mode and improve app dashboard scroll presentation
- Update dees-chart-log to refresh the terminal theme when goBright changes and derive dark mode directly from the brightness setting.
- Refine dees-simple-appdash control bar colors, borders, and shadow gradients for better light and dark theme consistency.
- Expand the app dashboard demo with recent activity content to showcase scrollable layout behavior.
## 2026-04-03 - 3.52.1 - fix(dees-modal)
refine modal styling, spacing, and animations for a cleaner overlay presentation

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.52.1",
"version": "3.54.0",
"private": false,
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
"main": "dist_ts_web/index.js",

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
version: '3.52.1',
version: '3.54.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@@ -70,7 +70,7 @@ export class DeesAppuiMaincontent extends DeesElement {
grid-template-rows: auto 1fr;
width: 100%;
height: 100%;
background: ${cssManager.bdTheme('#ffffff', '#161616')};
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
}
.maincontainer {

View File

@@ -458,6 +458,9 @@ export class DeesChartLog extends DeesElement {
public updated(changedProperties: Map<string, any>) {
super.updated(changedProperties);
if (changedProperties.has('goBright') && this.terminal) {
this.terminal.options.theme = this.getTerminalTheme();
}
if (changedProperties.has('logEntries') && this.terminalReady && this.logEntries.length > 0) {
const oldEntries: ILogEntry[] = changedProperties.get('logEntries') || [];
const newEntries = this.logEntries;
@@ -504,7 +507,7 @@ export class DeesChartLog extends DeesElement {
}
private getTerminalTheme() {
const isDark = this.domtoolsInstance?.themeManager?.isDarkMode ?? true;
const isDark = !this.goBright;
return isDark
? {
background: '#0a0a0a',

View File

@@ -53,11 +53,9 @@ export class DeesDataviewCodebox extends DeesElement {
text-align: left;
font-size: 16px;
font-family: ${cssGeistFontFamily};
height: 100%;
box-sizing: border-box;
}
dees-tile {
height: 100%;
color: ${cssManager.bdTheme('#09090b', '#fafafa')};
}
@@ -117,8 +115,6 @@ export class DeesDataviewCodebox extends DeesElement {
display: grid;
grid-template-columns: 50px auto;
overflow: auto;
flex: 1;
min-height: 0;
}
.lineNumbers {

View File

@@ -214,9 +214,8 @@ export class DeesInputCode extends DeesInputBase<string> {
}
.editor-wrapper {
position: relative;
flex: 1;
min-height: 0;
position: absolute;
inset: 0;
}
dees-workspace-monaco {

View File

@@ -239,6 +239,7 @@ export class DeesInputRichtext extends DeesInputBase<string> {
this.editorElement = this.shadowRoot!.querySelector('.editor-content')!;
this.linkInputElement = this.shadowRoot!.querySelector('.link-input input')!;
this.initializeEditor();
this.requestUpdate();
}
private initializeEditor(): void {

View File

@@ -90,10 +90,10 @@ export const richtextStyles = [
}
.editor-content {
flex: 1;
position: absolute;
inset: 0;
padding: 16px;
overflow-y: auto;
min-height: var(--min-height, 200px);
}
.editor-content .ProseMirror {
@@ -101,6 +101,8 @@ export const richtextStyles = [
line-height: 1.6;
color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')};
min-height: 100%;
white-space: pre-wrap;
word-wrap: break-word;
}
.editor-content .ProseMirror p {
@@ -281,7 +283,7 @@ export const richtextStyles = [
line-height: 1.4;
}
:host([disabled]) .editor-container {
:host([disabled]) dees-tile {
opacity: 0.6;
cursor: not-allowed;
}

View File

@@ -3,6 +3,7 @@ import { PdfManager } from '../dees-pdf-shared/PdfManager.js';
import { viewerStyles } from './styles.js';
import { demo as demoFunc } from './demo.js';
import '../../00group-utility/dees-icon/dees-icon.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global {
interface HTMLElementTagNameMap {
@@ -33,6 +34,9 @@ export class DeesPdfViewer extends DeesElement {
@property({ type: Boolean })
accessor showSidebar: boolean = false;
@property({ type: String })
accessor sidebarPosition: 'left' | 'right' = 'left';
@property({ type: Number })
accessor currentPage: number = 1;
@@ -54,6 +58,9 @@ export class DeesPdfViewer extends DeesElement {
@property({ type: Array })
accessor pageData: Array<{page: number, rendered: boolean, rendering: boolean, textLayerRendered: boolean}> = [];
@property({ type: Number })
accessor pdfFileSize: number = 0;
private pdfDocument: any;
private renderState: RenderState = 'idle';
private renderAbortController: AbortController | null = null;
@@ -85,9 +92,9 @@ export class DeesPdfViewer extends DeesElement {
public render(): TemplateResult {
return html`
<div class="pdf-viewer ${this.showSidebar ? 'with-sidebar' : ''}">
<dees-tile class="${this.showSidebar ? 'with-sidebar' : ''} sidebar-${this.sidebarPosition}">
${this.showToolbar ? html`
<div class="toolbar">
<div slot="header" class="toolbar">
<div class="toolbar-group">
<button
class="toolbar-button"
@@ -240,7 +247,23 @@ export class DeesPdfViewer extends DeesElement {
`}
</div>
</div>
</div>
<div slot="footer" class="pdf-footer">
<div class="pdf-footer-left">
<span class="pdf-footer-item">Zoom ${Math.round(this.currentZoom * 100)}%</span>
${this.pdfFileSize > 0 ? html`
<span class="pdf-footer-item">${this.formatFileSize(this.pdfFileSize)}</span>
` : ''}
</div>
<div class="pdf-footer-center" style="margin-left: ${this.showSidebar && this.sidebarPosition === 'left' ? '100px' : this.showSidebar && this.sidebarPosition === 'right' ? '-100px' : '0'}">
<span>Page ${this.currentPage} of ${this.totalPages}</span>
</div>
<div class="pdf-footer-right">
${this.pdfUrl ? html`
<span class="pdf-footer-filename">${this.pdfUrl.split('/').pop()}</span>
` : ''}
</div>
</div>
</dees-tile>
`;
}
@@ -305,6 +328,12 @@ export class DeesPdfViewer extends DeesElement {
}
}
private formatFileSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
private async loadPdf() {
this.loading = true;
this.renderState = 'loading';
@@ -323,6 +352,14 @@ export class DeesPdfViewer extends DeesElement {
this.currentPage = this.initialPage;
this.resolveInitialViewportMode();
// Get file size
try {
const data = await this.pdfDocument.getData();
this.pdfFileSize = data.length;
} catch (e) {
this.pdfFileSize = 0;
}
// Initialize thumbnail and page data arrays
this.thumbnailData = Array.from({length: this.totalPages}, (_, i) => ({
page: i + 1,
@@ -699,8 +736,8 @@ export class DeesPdfViewer extends DeesElement {
const viewerRect = this.viewerMain.getBoundingClientRect();
const currentScrollTop = this.viewerMain.scrollTop;
// Calculate the target scroll position
const targetScrollTop = currentScrollTop + (pageRect.top - viewerRect.top) - this.viewerMain.clientTop;
// Calculate the target scroll position (offset by 16px so page doesn't touch the top edge)
const targetScrollTop = currentScrollTop + (pageRect.top - viewerRect.top) - this.viewerMain.clientTop - 16;
// Scroll to the calculated position
if (smooth) {

View File

@@ -3,8 +3,8 @@ import { html } from '@design.estate/dees-element';
export const demo = () => html`
<style>
.demo-container {
padding: 40px;
background: #f5f5f5;
padding: 20px;
background: #000000;
}
.demo-section {
@@ -15,6 +15,7 @@ export const demo = () => html`
margin-bottom: 20px;
font-size: 18px;
font-weight: 600;
color: #fafafa;
}
dees-pdf-viewer {

View File

@@ -12,25 +12,47 @@ export const viewerStyles = [
contain: layout style;
}
.pdf-viewer {
width: 100%;
dees-tile {
height: 100%;
display: flex;
flex-direction: column;
background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(215 20% 10%)')};
position: relative;
overflow: hidden;
}
.viewer-container::before,
.viewer-container::after {
content: '';
position: absolute;
left: 0;
right: 0;
height: 8px;
z-index: 5;
pointer-events: none;
}
.viewer-container::before {
top: 0;
background: linear-gradient(
to bottom,
${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 0% / 0.4)')},
${cssManager.bdTheme('hsl(0 0% 0% / 0.03)', 'hsl(0 0% 0% / 0.12)')},
transparent
);
}
.viewer-container::after {
bottom: 0;
background: linear-gradient(
to top,
${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 0% / 0.4)')},
${cssManager.bdTheme('hsl(0 0% 0% / 0.03)', 'hsl(0 0% 0% / 0.12)')},
transparent
);
}
.toolbar {
height: 48px;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20% 15%)')};
border-bottom: 1px solid ${cssManager.bdTheme('hsl(214 31% 91%)', 'hsl(217 25% 22%)')};
height: 40px;
display: flex;
align-items: center;
padding: 0 16px;
gap: 16px;
flex-shrink: 0;
padding: 0 12px;
gap: 12px;
}
.toolbar-group {
@@ -108,21 +130,27 @@ export const viewerStyles = [
}
.viewer-container {
flex: 1;
position: absolute;
inset: 0;
display: flex;
overflow: hidden;
position: relative;
min-height: 0;
}
.sidebar {
width: 200px;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20% 15%)')};
background: transparent;
border-right: 1px solid ${cssManager.bdTheme('hsl(214 31% 91%)', 'hsl(217 25% 22%)')};
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
order: 0;
}
.sidebar-right .sidebar {
border-right: none;
border-left: 1px solid ${cssManager.bdTheme('hsl(214 31% 91%)', 'hsl(217 25% 22%)')};
order: 1;
}
.sidebar-header {
@@ -334,5 +362,52 @@ export const viewerStyles = [
.pdf-viewer.with-sidebar .viewer-main {
margin-left: 0;
}
.pdf-footer {
height: 28px;
padding: 0 16px;
display: flex;
align-items: center;
font-size: 11px;
color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')};
width: 100%;
box-sizing: border-box;
position: relative;
}
.pdf-footer-left {
display: flex;
align-items: center;
gap: 12px;
}
.pdf-footer-left .pdf-footer-item + .pdf-footer-item {
padding-left: 12px;
border-left: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
}
.pdf-footer-center {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-weight: 500;
transition: margin-left 0.15s ease;
}
.pdf-footer-right {
margin-left: auto;
}
.pdf-footer-item {
white-space: nowrap;
}
.pdf-footer-filename {
font-family: 'Intel One Mono', 'Geist Mono', monospace;
opacity: 0.7;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`,
];

View File

@@ -8,22 +8,22 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { demo } from './demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-tile-audio': DeesTileAudio;
'dees-thumbnail-audio': DeesThumbnailAudio;
}
}
@customElement('dees-tile-audio')
export class DeesTileAudio extends DeesTileBase {
@customElement('dees-thumbnail-audio')
export class DeesThumbnailAudio extends DeesThumbnailBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
...thumbnailBaseStyles,
css`
.audio-content {
position: relative;

View File

@@ -26,51 +26,51 @@ export const demo = () => html`
<div class="demo-section">
<h3>Audio Tiles</h3>
<div class="tile-row">
<dees-tile-audio
<dees-thumbnail-audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
title="SoundHelix Song 1"
artist="T. Schuerger"
label="soundhelix-1.mp3"
@tile-click=${(e: CustomEvent) => console.log('Audio clicked:', e.detail)}
></dees-tile-audio>
></dees-thumbnail-audio>
<dees-tile-audio
<dees-thumbnail-audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3"
title="SoundHelix Song 2"
artist="T. Schuerger"
label="soundhelix-2.mp3"
></dees-tile-audio>
></dees-thumbnail-audio>
<dees-tile-audio
<dees-thumbnail-audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"
title="SoundHelix Song 3"
label="soundhelix-3.mp3"
></dees-tile-audio>
></dees-thumbnail-audio>
</div>
</div>
<div class="demo-section">
<h3>Size Variants</h3>
<div class="tile-row">
<dees-tile-audio
<dees-thumbnail-audio
size="small"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
title="Small"
label="small.mp3"
></dees-tile-audio>
></dees-thumbnail-audio>
<dees-tile-audio
<dees-thumbnail-audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
title="Default"
label="default.mp3"
></dees-tile-audio>
></dees-thumbnail-audio>
<dees-tile-audio
<dees-thumbnail-audio
size="large"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
title="Large"
label="large.mp3"
></dees-tile-audio>
></dees-thumbnail-audio>
</div>
</div>
</div>

View File

@@ -7,11 +7,11 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { demo } from './demo.js';
export interface ITileFolderItem {
export interface IThumbnailFolderItem {
type: 'pdf' | 'image' | 'audio' | 'video' | 'note' | 'folder' | 'unknown';
thumbnailSrc?: string;
name: string;
@@ -29,16 +29,16 @@ const TYPE_ICON_MAP: Record<string, string> = {
declare global {
interface HTMLElementTagNameMap {
'dees-tile-folder': DeesTileFolder;
'dees-thumbnail-folder': DeesThumbnailFolder;
}
}
@customElement('dees-tile-folder')
export class DeesTileFolder extends DeesTileBase {
@customElement('dees-thumbnail-folder')
export class DeesThumbnailFolder extends DeesThumbnailBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
...thumbnailBaseStyles,
css`
.folder-content {
position: relative;
@@ -116,7 +116,7 @@ export class DeesTileFolder extends DeesTileBase {
accessor name: string = '';
@property({ attribute: false })
accessor items: ITileFolderItem[] = [];
accessor items: IThumbnailFolderItem[] = [];
protected renderTileContent(): TemplateResult {
const previewItems = this.items.slice(0, 4);

View File

@@ -1,8 +1,8 @@
import { html } from '@design.estate/dees-element';
import type { ITileFolderItem } from './component.js';
import type { IThumbnailFolderItem } from './component.js';
export const demo = () => {
const photosFolder: ITileFolderItem[] = [
const photosFolder: IThumbnailFolderItem[] = [
{ type: 'image', name: 'sunset.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=1' },
{ type: 'image', name: 'mountain.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=2' },
{ type: 'image', name: 'ocean.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=3' },
@@ -11,7 +11,7 @@ export const demo = () => {
{ type: 'image', name: 'desert.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=6' },
];
const projectFolder: ITileFolderItem[] = [
const projectFolder: IThumbnailFolderItem[] = [
{ type: 'note', name: 'README.md' },
{ type: 'note', name: 'package.json' },
{ type: 'folder', name: 'src' },
@@ -21,16 +21,16 @@ export const demo = () => {
{ type: 'image', name: 'logo.png', thumbnailSrc: 'https://picsum.photos/100/100?random=10' },
];
const mediaFolder: ITileFolderItem[] = [
const mediaFolder: IThumbnailFolderItem[] = [
{ type: 'video', name: 'intro.mp4' },
{ type: 'audio', name: 'background.mp3' },
{ type: 'image', name: 'thumbnail.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=20' },
{ type: 'pdf', name: 'storyboard.pdf' },
];
const emptyFolder: ITileFolderItem[] = [];
const emptyFolder: IThumbnailFolderItem[] = [];
const singleItemFolder: ITileFolderItem[] = [
const singleItemFolder: IThumbnailFolderItem[] = [
{ type: 'pdf', name: 'report.pdf' },
];
@@ -60,61 +60,61 @@ export const demo = () => {
<div class="demo-section">
<h3>Folder Tiles</h3>
<div class="tile-row">
<dees-tile-folder
<dees-thumbnail-folder
name="Photos"
.items=${photosFolder}
label="6 photos"
@tile-click=${(e: CustomEvent) => console.log('Folder clicked:', e.detail)}
></dees-tile-folder>
></dees-thumbnail-folder>
<dees-tile-folder
<dees-thumbnail-folder
name="my-project"
.items=${projectFolder}
label="Project files"
></dees-tile-folder>
></dees-thumbnail-folder>
<dees-tile-folder
<dees-thumbnail-folder
name="Media Assets"
.items=${mediaFolder}
label="Mixed media"
></dees-tile-folder>
></dees-thumbnail-folder>
</div>
</div>
<div class="demo-section">
<h3>Edge Cases</h3>
<div class="tile-row">
<dees-tile-folder
<dees-thumbnail-folder
name="Empty Folder"
.items=${emptyFolder}
></dees-tile-folder>
></dees-thumbnail-folder>
<dees-tile-folder
<dees-thumbnail-folder
name="Single Item"
.items=${singleItemFolder}
></dees-tile-folder>
></dees-thumbnail-folder>
</div>
</div>
<div class="demo-section">
<h3>Size Variants</h3>
<div class="tile-row">
<dees-tile-folder
<dees-thumbnail-folder
size="small"
name="Small"
.items=${photosFolder}
></dees-tile-folder>
></dees-thumbnail-folder>
<dees-tile-folder
<dees-thumbnail-folder
name="Default"
.items=${photosFolder}
></dees-tile-folder>
></dees-thumbnail-folder>
<dees-tile-folder
<dees-thumbnail-folder
size="large"
name="Large"
.items=${photosFolder}
></dees-tile-folder>
></dees-thumbnail-folder>
</div>
</div>
</div>

View File

@@ -8,22 +8,22 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { demo } from './demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-tile-image': DeesTileImage;
'dees-thumbnail-image': DeesThumbnailImage;
}
}
@customElement('dees-tile-image')
export class DeesTileImage extends DeesTileBase {
@customElement('dees-thumbnail-image')
export class DeesThumbnailImage extends DeesThumbnailBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
...thumbnailBaseStyles,
css`
.image-wrapper {
position: relative;

View File

@@ -26,59 +26,59 @@ export const demo = () => html`
<div class="demo-section">
<h3>Image Tiles</h3>
<div class="tile-row">
<dees-tile-image
<dees-thumbnail-image
src="https://picsum.photos/800/600"
alt="Landscape photo"
label="landscape.jpg"
@tile-click=${(e: CustomEvent) => console.log('Image clicked:', e.detail)}
></dees-tile-image>
></dees-thumbnail-image>
<dees-tile-image
<dees-thumbnail-image
src="https://picsum.photos/400/400"
alt="Square photo"
label="square.png"
></dees-tile-image>
></dees-thumbnail-image>
<dees-tile-image
<dees-thumbnail-image
src="https://picsum.photos/300/900"
alt="Portrait photo"
label="portrait.webp"
></dees-tile-image>
></dees-thumbnail-image>
</div>
</div>
<div class="demo-section">
<h3>Size Variants</h3>
<div class="tile-row">
<dees-tile-image
<dees-thumbnail-image
size="small"
src="https://picsum.photos/200/200"
alt="Small"
label="small.jpg"
></dees-tile-image>
></dees-thumbnail-image>
<dees-tile-image
<dees-thumbnail-image
src="https://picsum.photos/600/400"
alt="Default"
label="default.jpg"
></dees-tile-image>
></dees-thumbnail-image>
<dees-tile-image
<dees-thumbnail-image
size="large"
src="https://picsum.photos/1200/800"
alt="Large"
label="large.jpg"
></dees-tile-image>
></dees-thumbnail-image>
</div>
</div>
<div class="demo-section">
<h3>Error State (broken URL)</h3>
<dees-tile-image
<dees-thumbnail-image
src="https://invalid-url-that-does-not-exist.example/image.png"
alt="Broken"
label="broken.png"
></dees-tile-image>
></dees-thumbnail-image>
</div>
</div>
`;

View File

@@ -8,22 +8,22 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { demo } from './demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-tile-note': DeesTileNote;
'dees-thumbnail-note': DeesThumbnailNote;
}
}
@customElement('dees-tile-note')
export class DeesTileNote extends DeesTileBase {
@customElement('dees-thumbnail-note')
export class DeesThumbnailNote extends DeesThumbnailBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
...thumbnailBaseStyles,
css`
.note-content {
position: relative;

View File

@@ -72,63 +72,63 @@ Action Items:
<div class="demo-section">
<h3>Note Tiles</h3>
<div class="tile-row">
<dees-tile-note
<dees-thumbnail-note
title="component.ts"
.content=${sampleCode}
language="typescript"
label="component.ts"
@tile-click=${(e: CustomEvent) => console.log('Note clicked:', e.detail)}
></dees-tile-note>
></dees-thumbnail-note>
<dees-tile-note
<dees-thumbnail-note
title="Meeting Notes"
.content=${sampleText}
label="meeting-notes.txt"
></dees-tile-note>
></dees-thumbnail-note>
<dees-tile-note
<dees-thumbnail-note
title="package.json"
.content=${sampleJson}
language="json"
label="package.json"
></dees-tile-note>
></dees-thumbnail-note>
</div>
</div>
<div class="demo-section">
<h3>Size Variants</h3>
<div class="tile-row">
<dees-tile-note
<dees-thumbnail-note
size="small"
title="small.ts"
.content=${sampleCode}
language="ts"
label="small.ts"
></dees-tile-note>
></dees-thumbnail-note>
<dees-tile-note
<dees-thumbnail-note
title="default.ts"
.content=${sampleCode}
language="ts"
label="default.ts"
></dees-tile-note>
></dees-thumbnail-note>
<dees-tile-note
<dees-thumbnail-note
size="large"
title="large.ts"
.content=${sampleCode}
language="ts"
label="large.ts"
></dees-tile-note>
></dees-thumbnail-note>
</div>
</div>
<div class="demo-section">
<h3>Without Title</h3>
<dees-tile-note
<dees-thumbnail-note
.content=${sampleText}
label="untitled.txt"
></dees-tile-note>
></dees-thumbnail-note>
</div>
</div>
`;

View File

@@ -1,6 +1,6 @@
import { property, state, html, customElement, type TemplateResult, type CSSResult } from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { PdfManager } from '../dees-pdf-shared/PdfManager.js';
import { CanvasPool, type PooledCanvas } from '../dees-pdf-shared/CanvasPool.js';
import { PerformanceMonitor, throttle, formatFileSize } from '../dees-pdf-shared/utils.js';
@@ -9,15 +9,15 @@ import { demo as demoFunc } from './demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-tile-pdf': DeesTilePdf;
'dees-thumbnail-pdf': DeesThumbnailPdf;
}
}
@customElement('dees-tile-pdf')
export class DeesTilePdf extends DeesTileBase {
@customElement('dees-thumbnail-pdf')
export class DeesThumbnailPdf extends DeesThumbnailBase {
public static demo = demoFunc;
public static demoGroups = ['Media', 'PDF'];
public static styles = [...tileBaseStyles, tilePdfStyles] as any;
public static styles = [...thumbnailBaseStyles, tilePdfStyles] as any;
@property({ type: String })
accessor pdfUrl: string = '';

View File

@@ -11,7 +11,7 @@ export const demo = () => {
for (let i = 0; i < count; i++) {
const pdfUrl = samplePdfs[i % samplePdfs.length];
items.push(html`
<dees-tile-pdf
<dees-thumbnail-pdf
pdfUrl="${pdfUrl}"
clickable="true"
grid-mode
@@ -19,7 +19,7 @@ export const demo = () => {
console.log('PDF Tile clicked:', e.detail);
alert(`PDF clicked: ${e.detail.pageCount} pages`);
}}
></dees-tile-pdf>
></dees-thumbnail-pdf>
`);
}
return items;
@@ -67,56 +67,56 @@ export const demo = () => {
<div class="demo-container">
<div class="demo-section">
<h3>Single PDF Tile</h3>
<dees-tile-pdf
<dees-thumbnail-pdf
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
clickable="true"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
<div class="demo-section">
<h3>Different Sizes</h3>
<div class="preview-row">
<div class="preview-label">Small:</div>
<dees-tile-pdf
<dees-thumbnail-pdf
size="small"
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
clickable="true"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
<div class="preview-row">
<div class="preview-label">Default:</div>
<dees-tile-pdf
<dees-thumbnail-pdf
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
clickable="true"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
<div class="preview-row">
<div class="preview-label">Large:</div>
<dees-tile-pdf
<dees-thumbnail-pdf
size="large"
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
clickable="true"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
</div>
<div class="demo-section">
<h3>With Label</h3>
<dees-tile-pdf
<dees-thumbnail-pdf
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
clickable="true"
label="Research Paper.pdf"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
<div class="demo-section">
<h3>Non-Clickable</h3>
<dees-tile-pdf
<dees-thumbnail-pdf
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
clickable="false"
></dees-tile-pdf>
></dees-thumbnail-pdf>
</div>
<div class="demo-section">

View File

@@ -5,11 +5,11 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { tileBaseStyles } from './styles.js';
import { thumbnailBaseStyles } from './styles.js';
import '../../00group-utility/dees-icon/dees-icon.js';
export abstract class DeesTileBase extends DeesElement {
public static styles: CSSResult[] = tileBaseStyles as any;
export abstract class DeesThumbnailBase extends DeesElement {
public static styles: CSSResult[] = thumbnailBaseStyles as any;
@property({ type: Boolean })
accessor clickable: boolean = true;

View File

@@ -0,0 +1,2 @@
export { DeesThumbnailBase } from './DeesThumbnailBase.js';
export { thumbnailBaseStyles } from './styles.js';

View File

@@ -1,6 +1,6 @@
import { css, cssManager } from '@design.estate/dees-element';
export const tileBaseStyles = [
export const thumbnailBaseStyles = [
cssManager.defaultStyles,
css`
:host {

View File

@@ -8,22 +8,22 @@ import {
type TemplateResult,
type CSSResult,
} from '@design.estate/dees-element';
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
import { demo } from './demo.js';
declare global {
interface HTMLElementTagNameMap {
'dees-tile-video': DeesTileVideo;
'dees-thumbnail-video': DeesThumbnailVideo;
}
}
@customElement('dees-tile-video')
export class DeesTileVideo extends DeesTileBase {
@customElement('dees-thumbnail-video')
export class DeesThumbnailVideo extends DeesThumbnailBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
...thumbnailBaseStyles,
css`
.video-wrapper {
position: relative;

View File

@@ -26,54 +26,54 @@ export const demo = () => html`
<div class="demo-section">
<h3>Video Tiles</h3>
<div class="tile-row">
<dees-tile-video
<dees-thumbnail-video
src="https://www.w3schools.com/html/mov_bbb.mp4"
label="bunny.mp4"
@tile-click=${(e: CustomEvent) => console.log('Video clicked:', e.detail)}
></dees-tile-video>
></dees-thumbnail-video>
<dees-tile-video
<dees-thumbnail-video
src="https://www.w3schools.com/html/movie.mp4"
poster="https://picsum.photos/400/300"
label="movie.mp4"
></dees-tile-video>
></dees-thumbnail-video>
<dees-tile-video
<dees-thumbnail-video
src="https://www.w3schools.com/html/mov_bbb.mp4"
label="another-video.mp4"
></dees-tile-video>
></dees-thumbnail-video>
</div>
</div>
<div class="demo-section">
<h3>Size Variants</h3>
<div class="tile-row">
<dees-tile-video
<dees-thumbnail-video
size="small"
src="https://www.w3schools.com/html/mov_bbb.mp4"
label="small.mp4"
></dees-tile-video>
></dees-thumbnail-video>
<dees-tile-video
<dees-thumbnail-video
src="https://www.w3schools.com/html/mov_bbb.mp4"
label="default.mp4"
></dees-tile-video>
></dees-thumbnail-video>
<dees-tile-video
<dees-thumbnail-video
size="large"
src="https://www.w3schools.com/html/mov_bbb.mp4"
label="large.mp4"
></dees-tile-video>
></dees-thumbnail-video>
</div>
</div>
<div class="demo-section">
<h3>With Poster Image</h3>
<dees-tile-video
<dees-thumbnail-video
src="https://www.w3schools.com/html/movie.mp4"
poster="https://picsum.photos/600/400"
label="poster-video.mp4"
></dees-tile-video>
></dees-thumbnail-video>
</div>
</div>
`;

View File

@@ -1,2 +0,0 @@
export { DeesTileBase } from './DeesTileBase.js';
export { tileBaseStyles } from './styles.js';

View File

@@ -8,11 +8,11 @@ export * from './dees-preview/index.js';
export * from './dees-pdf-shared/index.js';
export * from './dees-pdf-viewer/index.js';
// Tile Components
export * from './dees-tile-shared/index.js';
export * from './dees-tile-pdf/index.js';
export * from './dees-tile-image/index.js';
export * from './dees-tile-audio/index.js';
export * from './dees-tile-video/index.js';
export * from './dees-tile-note/index.js';
export * from './dees-tile-folder/index.js';
// Thumbnail Components
export * from './dees-thumbnail-shared/index.js';
export * from './dees-thumbnail-pdf/index.js';
export * from './dees-thumbnail-image/index.js';
export * from './dees-thumbnail-audio/index.js';
export * from './dees-thumbnail-video/index.js';
export * from './dees-thumbnail-note/index.js';
export * from './dees-thumbnail-folder/index.js';

View File

@@ -110,6 +110,48 @@ class DemoViewDashboard extends DeesElement {
console.log('Tile action:', e.detail);
}}
></dees-statsgrid>
<h2 style="margin-top: 40px;">Recent Activity</h2>
<p>Below is a log of recent system events and user activity to demonstrate scrollable content.</p>
${[
{ time: '2 min ago', event: 'User john@example.com logged in from 192.168.1.42', type: 'info' },
{ time: '5 min ago', event: 'Deployment v3.52.1 completed successfully on production', type: 'success' },
{ time: '12 min ago', event: 'Database backup finished — 2.4 GB compressed', type: 'info' },
{ time: '18 min ago', event: 'SSL certificate renewed for api.example.com (expires 2027-04-03)', type: 'success' },
{ time: '25 min ago', event: 'Memory usage spike on worker-03 (92%) — auto-scaled to 4 instances', type: 'warning' },
{ time: '31 min ago', event: 'New user registration: sarah@company.io', type: 'info' },
{ time: '45 min ago', event: 'Scheduled job "cleanup-temp-files" completed — removed 1,247 files', type: 'info' },
{ time: '1 hour ago', event: 'API rate limit reached for client app-mobile-ios (429 responses)', type: 'warning' },
{ time: '1.5 hours ago', event: 'CDN cache purged for /assets/* — 340 objects invalidated', type: 'info' },
{ time: '2 hours ago', event: 'Failed login attempt for admin@example.com from 203.0.113.50 (blocked)', type: 'error' },
{ time: '2.5 hours ago', event: 'Webhook delivery to https://hooks.slack.com succeeded (200 OK)', type: 'info' },
{ time: '3 hours ago', event: 'Cron job "generate-reports" started — processing Q1 2026 data', type: 'info' },
{ time: '3.5 hours ago', event: 'Load balancer health check: all 8 nodes healthy', type: 'success' },
{ time: '4 hours ago', event: 'DNS propagation complete for new subdomain staging.example.com', type: 'success' },
].map(item => html`
<div style="
display: flex;
align-items: baseline;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(0 0% 12%)')};
font-size: 13px;
">
<span style="
flex-shrink: 0;
width: 100px;
font-size: 11px;
color: ${cssManager.bdTheme('hsl(0 0% 55%)', 'hsl(0 0% 45%)')};
">${item.time}</span>
<span style="
color: ${item.type === 'error' ? cssManager.bdTheme('hsl(0 72% 50%)', 'hsl(0 72% 65%)') :
item.type === 'warning' ? cssManager.bdTheme('hsl(25 95% 50%)', 'hsl(25 95% 63%)') :
item.type === 'success' ? cssManager.bdTheme('hsl(142 70% 40%)', 'hsl(142 70% 55%)') :
cssManager.bdTheme('hsl(0 0% 30%)', 'hsl(0 0% 75%)')};
">${item.event}</span>
</div>
`)}
`;
}
}

View File

@@ -299,15 +299,51 @@ export class DeesSimpleAppDash extends DeesElement {
overscroll-behavior: contain;
}
.appcontent::before {
content: '';
position: sticky;
top: 0;
left: 0;
right: 0;
display: block;
height: 8px;
margin-bottom: -8px;
z-index: 10;
pointer-events: none;
background: linear-gradient(
to bottom,
${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 0% / 0.4)')},
${cssManager.bdTheme('hsl(0 0% 0% / 0.03)', 'hsl(0 0% 0% / 0.12)')},
transparent
);
}
.controlbar::before {
content: '';
position: absolute;
top: -8px;
left: 240px;
right: 0;
height: 8px;
pointer-events: none;
background: linear-gradient(
to top,
${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 0% / 0.4)')},
${cssManager.bdTheme('hsl(0 0% 0% / 0.03)', 'hsl(0 0% 0% / 0.12)')},
transparent
);
}
.controlbar {
color: #fff;
color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')};
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 24px;
background: ${cssManager.bdTheme('hsl(220 13% 18%)', 'hsl(220 13% 12%)')};
z-index: 2;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')};
border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
z-index: 11;
display: flex;
justify-content: flex-end;
align-items: center;
@@ -325,9 +361,10 @@ export class DeesSimpleAppDash extends DeesElement {
height: 100%;
white-space: nowrap;
cursor: default;
color: hsl(0 0% 70%);
color: ${cssManager.bdTheme('hsl(0 0% 55%)', 'hsl(0 0% 50%)')};
transition: all 0.15s ease;
border-left: 1px solid hsl(0 0% 100% / 0.08);
border-left: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
}
.control:first-child {
@@ -335,8 +372,8 @@ export class DeesSimpleAppDash extends DeesElement {
}
.control:hover {
background: hsl(0 0% 100% / 0.06);
color: hsl(0 0% 95%);
background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.04)', 'hsl(0 0% 100% / 0.06)')};
color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')};
}
.control dees-icon {

View File

@@ -94,8 +94,8 @@ export class DeesWorkspaceTerminalPreview extends DeesElement {
}
.terminal-container {
flex: 1;
position: relative;
position: absolute;
inset: 0;
padding: 8px;
}

View File

@@ -310,23 +310,16 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
}
this.tiptapLoadingPromise = (async () => {
const version = CDN_VERSIONS.tiptap;
// Load all Tiptap modules in parallel
const [
coreModule,
starterKitModule,
underlineModule,
textAlignModule,
linkModule,
typographyModule,
] = await Promise.all([
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/core@${version}/+esm`),
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/starter-kit@${version}/+esm`),
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-underline@${version}/+esm`),
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-text-align@${version}/+esm`),
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-link@${version}/+esm`),
import(/* @vite-ignore */ `${CDN_BASE}/@tiptap/extension-typography@${version}/+esm`),
// Import directly from npm packages — bundled by esbuild into a single
// module graph. CDN loading caused duplicate ProseMirror instances because
// jsdelivr resolves each package's dependencies independently.
const [coreModule, starterKitModule, underlineModule, textAlignModule, linkModule, typographyModule] = await Promise.all([
import('@tiptap/core'),
import('@tiptap/starter-kit'),
import('@tiptap/extension-underline'),
import('@tiptap/extension-text-align'),
import('@tiptap/extension-link'),
import('@tiptap/extension-typography'),
]);
this.tiptapLib = {