Compare commits

...

13 Commits

Author SHA1 Message Date
09f0aa97dd v3.42.0
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-02-02 14:32:20 +00:00
7c62f45d77 feat(dees-form-submit): forward button properties to internal dees-button, use property bindings, add demo and styles 2026-02-02 14:32:20 +00:00
b123768474 v3.41.6
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-02-02 09:27:53 +00:00
f292e7a7f4 fix(dees-simple-appdash): respect selectedView when loading initial view, falling back to the first tab 2026-02-02 09:27:53 +00:00
d82e5603a7 v3.41.5
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-02-01 23:51:53 +00:00
7e2386bcdf fix(dees-service-lib-loader): prevent horizontal scrollbar by offsetting xterm WidthCache measurement container 2026-02-01 23:51:53 +00:00
eba2a03355 v3.41.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-01-29 13:21:30 +00:00
06c01f0690 fix(): no changes 2026-01-29 13:21:30 +00:00
91e03eb9c4 v3.41.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-01-29 07:46:19 +00:00
b7f3f47c61 fix(dees-pdf-viewer): use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling 2026-01-29 07:46:19 +00:00
0a83f0e136 update 2026-01-28 17:10:37 +00:00
2b048cf34f update 2026-01-28 17:02:52 +00:00
7e50b8cb3f update 2026-01-28 16:46:07 +00:00
10 changed files with 568 additions and 49 deletions

View File

@@ -1,5 +1,41 @@
# Changelog
## 2026-02-02 - 3.42.0 - feat(dees-form-submit)
forward button properties to internal dees-button, use property bindings, add demo and styles
- Added forwarded properties: type, size, icon, iconPosition (with defaults) and preserved text/status/disabled
- Changed template to use property bindings (.prop) for dees-button instead of string attributes
- Switched internal event handler to listen for dees-button's @clicked event (was @click)
- Added component styles (:host display and dees-button width:100%) and improved layout
- Expanded demo with multiple usage examples (basic, icons, types, sizes, states, and a form context)
## 2026-02-02 - 3.41.6 - fix(dees-simple-appdash)
respect selectedView when loading initial view, falling back to the first tab
- firstUpdated now loads this.selectedView if set, otherwise loads the first view tab
- Prevents always loading the first tab and preserves a previously selected view on initial render
## 2026-02-01 - 3.41.5 - fix(dees-service-lib-loader)
prevent horizontal scrollbar by offsetting xterm WidthCache measurement container
- Injects additional CSS into DeesServiceLibLoader to move xterm.js WidthCache measurement div off-screen horizontally (selector: body > div[style*="top: -50000px"][style*="width: 50000px"])
- Fixes root cause where xterm creates a large-width measurement container (width: 50000px) on document.body that expands scrollWidth and causes a horizontal scrollbar
- Change applied in ts_web/services/DeesServiceLibLoader.ts by concatenating the fix CSS into the injected stylesheet
## 2026-01-29 - 3.41.4 - fix()
no changes
- No files changed in this commit; no code or documentation modified
- No release required
## 2026-01-29 - 3.41.3 - fix(dees-pdf-viewer)
use in-memory PDF data for download and print; add robust print wrapper, cleanup and error handling
- Download and Print now use pdfDocument.getData() to create Blob URLs so in-memory PDFs (pdf.js) can be saved/printed.
- Print flow now opens an HTML wrapper with an iframe to allow onafterprint handling, auto-close, popup-fallback and timed cleanup of Blob URLs.
- Added try/catch logging, URL.revokeObjectURL calls and safety timeouts to avoid resource leaks.
- Removed context menu items that relied on the raw PDF URL (Open in New Tab, Copy PDF URL); Download/Print actions now await the async handlers.
## 2026-01-28 - 3.41.2 - fix(dees-pdf-viewer)
account for devicePixelRatio when setting canvas dimensions and scale 2D context to render crisp PDF pages and thumbnails on high-DPI displays

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.41.2",
"version": "3.42.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

@@ -0,0 +1,79 @@
import { expect, tap, webhelpers } from '@git.zone/tstest/tapbundle';
import * as deesCatalog from '../ts_web/index.js';
tap.test('PDF viewer should render text layer', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
await viewer.updateComplete;
expect(viewer.totalPages).toBeGreaterThan(0);
const textLayer = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"]');
expect(textLayer).toBeTruthy();
const textSpans = textLayer?.querySelectorAll('span');
expect(textSpans?.length).toBeGreaterThan(0);
console.log(`Text layer has ${textSpans?.length} spans`);
});
tap.test('Text should be selectable', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
const textLayer = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"]');
const firstSpan = textLayer?.querySelector('span') as HTMLElement;
if (firstSpan?.textContent) {
const range = document.createRange();
const textNode = firstSpan.firstChild;
if (textNode) {
range.setStart(textNode, 0);
range.setEnd(textNode, Math.min(5, textNode.textContent?.length || 0));
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
expect(selection?.toString().length).toBeGreaterThan(0);
console.log('Selected text:', selection?.toString());
}
}
});
tap.test('endOfContent element exists for selection boundary', async () => {
const viewer = await webhelpers.fixture(
webhelpers.html`
<dees-pdf-viewer
pdfUrl="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
initialZoom="page-fit"
style="height: 600px; width: 100%;"
></dees-pdf-viewer>
`
) as deesCatalog.DeesPdfViewer;
// Wait for PDF to load and render
await new Promise(resolve => setTimeout(resolve, 5000));
const endOfContent = viewer.shadowRoot?.querySelector('.text-layer[data-page="1"] .endOfContent');
expect(endOfContent).toBeTruthy();
});
export default tap.start();

View File

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

View File

@@ -1,3 +1,85 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`<dees-form-submit>Submit Form</dees-form-submit>`;
export const demoFunc = () => html`
<style>
.demo-container {
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px;
}
.demo-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.demo-section h3 {
margin: 0 0 8px 0;
font-size: 14px;
font-weight: 500;
color: #888;
}
.demo-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Basic Usage</h3>
<div class="demo-row">
<dees-form-submit>Submit Form</dees-form-submit>
<dees-form-submit text="With Text Property"></dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>With Icons (inherited from DeesButton)</h3>
<div class="demo-row">
<dees-form-submit icon="lucide:send">Submit</dees-form-submit>
<dees-form-submit icon="lucide:save" iconPosition="left">Save Form</dees-form-submit>
<dees-form-submit icon="lucide:arrow-right" iconPosition="right">Continue</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>Button Types</h3>
<div class="demo-row">
<dees-form-submit type="highlighted" icon="lucide:send">Highlighted</dees-form-submit>
<dees-form-submit type="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit type="discreet" icon="lucide:send">Discreet</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
<dees-form-submit size="small" icon="lucide:send">Small</dees-form-submit>
<dees-form-submit size="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit size="large" icon="lucide:send">Large</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>States</h3>
<div class="demo-row">
<dees-form-submit status="normal" icon="lucide:send">Normal</dees-form-submit>
<dees-form-submit status="pending" icon="lucide:send">Pending</dees-form-submit>
<dees-form-submit status="success" icon="lucide:check">Success</dees-form-submit>
<dees-form-submit status="error" icon="lucide:x">Error</dees-form-submit>
<dees-form-submit disabled icon="lucide:send">Disabled</dees-form-submit>
</div>
</div>
<div class="demo-section">
<h3>In a Form Context</h3>
<dees-form>
<dees-input-text label="Name" key="name"></dees-input-text>
<dees-input-text label="Email" key="email"></dees-input-text>
<dees-form-submit icon="lucide:send" type="highlighted">Submit Form</dees-form-submit>
</dees-form>
</div>
</div>
`;

View File

@@ -6,6 +6,7 @@ import {
css,
cssManager,
property,
type TemplateResult,
} from '@design.estate/dees-element';
import type { DeesForm } from '../dees-form/dees-form.js';
import { themeDefaultStyles } from '../../00theme.js';
@@ -21,38 +22,61 @@ export class DeesFormSubmit extends DeesElement {
public static demo = demoFunc;
public static demoGroups = ['Form', 'Button'];
@property({
type: Boolean,
reflect: true,
})
// =============================================
// Properties forwarded to internal dees-button
// =============================================
@property({ type: Boolean, reflect: true })
accessor disabled = false;
@property({
type: String,
})
@property({ type: String })
accessor text: string;
@property({
type: String,
})
@property({ type: String })
accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
@property({ type: String, reflect: true })
accessor type: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link' | 'normal' | 'highlighted' | 'discreet' | 'big' = 'default';
@property({ type: String, reflect: true })
accessor size: 'sm' | 'default' | 'lg' | 'icon' | 'small' | 'normal' | 'large' = 'default';
@property({ type: String })
accessor icon: string;
@property({ type: String })
accessor iconPosition: 'left' | 'right' = 'left';
constructor() {
super();
}
public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
/* TODO: Migrate hardcoded values to --dees-* CSS variables */
`];
public render() {
public static styles = [
themeDefaultStyles,
cssManager.defaultStyles,
css`
:host {
display: inline-block;
}
dees-button {
width: 100%;
}
`,
];
public render(): TemplateResult {
return html`
<dees-button
status="${this.status}"
@click="${this.submit}"
?disabled="${this.disabled}"
.status=${this.status}
.type=${this.type}
.size=${this.size}
.icon=${this.icon}
.iconPosition=${this.iconPosition}
.text=${this.text}
?disabled=${this.disabled}
@clicked=${this.submit}
>
${this.text || html`<slot></slot>`}
<slot></slot>
</dees-button>
`;
}

View File

@@ -52,7 +52,7 @@ export class DeesPdfViewer extends DeesElement {
accessor thumbnailData: Array<{page: number, rendered: boolean}> = [];
@property({ type: Array })
accessor pageData: Array<{page: number, rendered: boolean, rendering: boolean}> = [];
accessor pageData: Array<{page: number, rendered: boolean, rendering: boolean, textLayerRendered: boolean}> = [];
private pdfDocument: any;
private renderState: RenderState = 'idle';
@@ -63,6 +63,7 @@ export class DeesPdfViewer extends DeesElement {
private currentRenderPromise: Promise<void> | null = null;
private thumbnailRenderTasks: any[] = [];
private pageRenderTasks: Map<number, any> = new Map();
private textLayerRenderTasks: Map<number, any> = new Map();
private canvas: HTMLCanvasElement | undefined;
private ctx: CanvasRenderingContext2D | undefined;
private viewerMain: HTMLElement | null = null;
@@ -230,6 +231,7 @@ export class DeesPdfViewer extends DeesElement {
<div class="page-wrapper" data-page="${item.page}">
<div class="canvas-container">
<canvas class="page-canvas" data-page="${item.page}"></canvas>
<div class="text-layer" data-page="${item.page}"></div>
</div>
</div>
`
@@ -330,7 +332,8 @@ export class DeesPdfViewer extends DeesElement {
this.pageData = Array.from({length: this.totalPages}, (_, i) => ({
page: i + 1,
rendered: false,
rendering: false
rendering: false,
textLayerRendered: false,
}));
// Set loading to false to render the pages
@@ -476,6 +479,9 @@ export class DeesPdfViewer extends DeesElement {
pageInfo.rendering = false;
this.pageRenderTasks.delete(pageNum);
// Render text layer for selection
await this.renderTextLayer(pageNum);
// Update page data to reflect rendered state
this.requestUpdate('pageData');
} catch (error: any) {
@@ -487,6 +493,132 @@ export class DeesPdfViewer extends DeesElement {
}
}
private async renderTextLayer(pageNum: number): Promise<void> {
const pageInfo = this.pageData.find(p => p.page === pageNum);
if (!pageInfo || pageInfo.textLayerRendered) return;
try {
const textLayerDiv = this.shadowRoot?.querySelector(
`.text-layer[data-page="${pageNum}"]`
) as HTMLElement;
if (!textLayerDiv) return;
textLayerDiv.innerHTML = '';
const page = await this.pdfDocument.getPage(pageNum);
const textContent = await page.getTextContent();
const viewport = this.computeViewport(page);
// @ts-ignore - Dynamic import of pdfjs
const pdfjs = await import('https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.379/+esm');
textLayerDiv.style.width = `${viewport.width}px`;
textLayerDiv.style.height = `${viewport.height}px`;
// Set the scale factor CSS variable - required by PDF.js text layer
textLayerDiv.style.setProperty('--scale-factor', String(viewport.scale));
const textLayerRenderTask = pdfjs.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: viewport,
});
this.textLayerRenderTasks.set(pageNum, textLayerRenderTask);
await textLayerRenderTask.promise;
// Add endOfContent for selection boundary
const endOfContent = document.createElement('div');
endOfContent.className = 'endOfContent';
textLayerDiv.appendChild(endOfContent);
// Custom drag selection for Shadow DOM compatibility
// caretRangeFromPoint doesn't pierce shadow DOM, so we find spans manually
let isDragging = false;
let anchorNode: Node | null = null;
let anchorOffset = 0;
const getTextPositionFromPoint = (x: number, y: number): { node: Node; offset: number } | null => {
// Find span at coordinates by checking bounding rects
const spans = Array.from(textLayerDiv.querySelectorAll('span'));
for (const span of spans) {
const rect = span.getBoundingClientRect();
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
const textNode = span.firstChild;
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
// Calculate character offset based on x position
const text = textNode.textContent || '';
const charWidth = rect.width / text.length;
const relativeX = x - rect.left;
const offset = Math.min(Math.round(relativeX / charWidth), text.length);
return { node: textNode, offset };
}
}
}
return null;
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
anchorNode = null;
textLayerDiv.classList.remove('selecting');
}
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mousemove', handleMouseMove);
};
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging || !anchorNode) return;
e.preventDefault();
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
const selection = window.getSelection();
if (selection) {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, pos.node, pos.offset);
} catch (err) {
// Ignore errors from invalid selections
}
}
}
};
textLayerDiv.addEventListener('mousedown', (e: MouseEvent) => {
if (e.button !== 0) return;
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
// Prevent native selection behavior
e.preventDefault();
isDragging = true;
anchorNode = pos.node;
anchorOffset = pos.offset;
textLayerDiv.classList.add('selecting');
// Clear existing selection
const selection = window.getSelection();
selection?.removeAllRanges();
// Add document-level listeners for drag
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
});
pageInfo.textLayerRendered = true;
page.cleanup?.();
this.textLayerRenderTasks.delete(pageNum);
} catch (error: any) {
if (error?.name !== 'RenderingCancelledException') {
console.error(`Error rendering text layer for page ${pageNum}:`, error);
}
this.textLayerRenderTasks.delete(pageNum);
}
}
private handleScroll = () => {
// Throttle scroll events
if (this.scrollThrottleTimeout) {
@@ -771,6 +903,7 @@ export class DeesPdfViewer extends DeesElement {
this.pageData.forEach(page => {
page.rendered = false;
page.rendering = false;
page.textLayerRendered = false;
});
// Cancel any ongoing render tasks
@@ -783,6 +916,16 @@ export class DeesPdfViewer extends DeesElement {
});
this.pageRenderTasks.clear();
// Cancel text layer render tasks
this.textLayerRenderTasks.forEach(task => {
try {
task.cancel?.();
} catch (error) {
// Ignore cancellation errors
}
});
this.textLayerRenderTasks.clear();
// Request update to re-render pages
this.requestUpdate();
@@ -792,52 +935,138 @@ export class DeesPdfViewer extends DeesElement {
});
}
private downloadPdf() {
const link = document.createElement('a');
link.href = this.pdfUrl;
link.download = this.pdfUrl.split('/').pop() || 'document.pdf';
link.click();
private async downloadPdf() {
if (!this.pdfDocument) return;
try {
// Get raw PDF data from the loaded document
const data = await this.pdfDocument.getData();
const blob = new Blob([data.buffer], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = this.pdfUrl ? this.pdfUrl.split('/').pop() || 'document.pdf' : 'document.pdf';
link.click();
// Clean up blob URL after short delay
setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
} catch (error) {
console.error('Error downloading PDF:', error);
}
}
private printPdf() {
window.open(this.pdfUrl, '_blank')?.print();
private async printPdf() {
if (!this.pdfDocument) return;
try {
// Get raw PDF data from the loaded document
const data = await this.pdfDocument.getData();
const blob = new Blob([data.buffer], { type: 'application/pdf' });
const pdfUrl = URL.createObjectURL(blob);
// Create an HTML wrapper page that embeds the PDF and handles print/close
// This gives us control over the afterprint event (direct PDF URLs don't support it)
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<title>Print PDF</title>
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; }
iframe { width: 100%; height: 100%; border: none; }
@media print {
html, body, iframe { width: 100%; height: 100%; }
}
</style>
</head>
<body>
<iframe src="${pdfUrl}" type="application/pdf"></iframe>
<script>
window.onload = function() {
setTimeout(function() {
window.focus();
window.print();
}, 500);
};
window.onafterprint = function() {
window.close();
};
// Safety close after 2 minutes
setTimeout(function() { window.close(); }, 120000);
</script>
</body>
</html>
`;
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
const htmlUrl = URL.createObjectURL(htmlBlob);
const printWindow = window.open(htmlUrl, '_blank', 'width=800,height=600');
if (printWindow) {
// Cleanup blob URLs when window closes
const checkClosed = setInterval(() => {
if (printWindow.closed) {
clearInterval(checkClosed);
URL.revokeObjectURL(pdfUrl);
URL.revokeObjectURL(htmlUrl);
}
}, 500);
// Safety cleanup after 2 minutes
setTimeout(() => {
clearInterval(checkClosed);
URL.revokeObjectURL(pdfUrl);
URL.revokeObjectURL(htmlUrl);
}, 120000);
} else {
// Popup blocked - fall back to direct navigation
window.open(pdfUrl, '_blank');
setTimeout(() => URL.revokeObjectURL(pdfUrl), 60000);
URL.revokeObjectURL(htmlUrl);
}
} catch (error) {
console.error('Error printing PDF:', error);
}
}
/**
* Provide context menu items for right-click functionality
*/
public getContextMenuItems() {
return [
{
name: 'Open PDF in New Tab',
iconName: 'lucide:ExternalLink',
action: async () => {
window.open(this.pdfUrl, '_blank');
}
},
{ divider: true },
{
name: 'Copy PDF URL',
const items: any[] = [];
// Add copy option if text is selected
const selection = window.getSelection();
const selectedText = selection?.toString() || '';
if (selectedText) {
items.push({
name: 'Copy',
iconName: 'lucide:Copy',
action: async () => {
await navigator.clipboard.writeText(this.pdfUrl);
await navigator.clipboard.writeText(selectedText);
}
},
});
items.push({ divider: true });
}
items.push(
{
name: 'Download PDF',
iconName: 'lucide:Download',
action: async () => {
this.downloadPdf();
await this.downloadPdf();
}
},
{
name: 'Print PDF',
iconName: 'lucide:Printer',
action: async () => {
this.printPdf();
await this.printPdf();
}
}
];
);
return items;
}
private get canZoomIn(): boolean {
@@ -996,6 +1225,16 @@ export class DeesPdfViewer extends DeesElement {
});
this.pageRenderTasks.clear();
// Cancel text layer render tasks
this.textLayerRenderTasks.forEach(task => {
try {
task.cancel?.();
} catch (error) {
// Ignore cancellation errors
}
});
this.textLayerRenderTasks.clear();
// Cancel any thumbnail render tasks
for (const task of (this.thumbnailRenderTasks || [])) {
try {

View File

@@ -276,6 +276,7 @@ export const viewerStyles = [
border-radius: 4px;
overflow: hidden;
display: inline-block;
position: relative;
}
.page-canvas {
@@ -284,6 +285,52 @@ export const viewerStyles = [
image-rendering: crisp-edges;
}
/* Text layer for selection */
.text-layer {
position: absolute;
inset: 0;
overflow: visible;
line-height: 1;
text-size-adjust: none;
forced-color-adjust: none;
transform-origin: 0 0;
z-index: 1;
user-select: text;
-webkit-user-select: text;
}
.text-layer span,
.text-layer br {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
user-select: text;
-webkit-user-select: text;
}
.text-layer ::selection {
background: rgba(0, 100, 200, 0.3);
}
.text-layer br::selection {
background: transparent;
}
.text-layer .endOfContent {
display: block;
position: absolute;
inset: 100% 0 0;
z-index: 0;
cursor: default;
user-select: none;
}
.text-layer.selecting .endOfContent {
top: 0;
}
.pdf-viewer.with-sidebar .viewer-main {
margin-left: 0;
}

View File

@@ -390,7 +390,8 @@ export class DeesSimpleAppDash extends DeesElement {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
if (this.viewTabs && this.viewTabs.length > 0) {
await this.loadView(this.viewTabs[0]);
const viewToLoad = this.selectedView || this.viewTabs[0];
await this.loadView(viewToLoad);
}
}

View File

@@ -193,9 +193,20 @@ export class DeesServiceLibLoader {
const response = await fetch(cssUrl);
const cssText = await response.text();
// Fix for xterm.js WidthCache measurement container causing horizontal scrollbar
// xterm.js creates this on document.body with width: 50000px, top: -50000px
// Moving it off-screen horizontally prevents scrollWidth expansion
const xtermMeasurementFix = `
/* Fix xterm.js WidthCache measurement container causing horizontal scrollbar */
/* xterm creates this on document.body - move it off-screen horizontally too */
body > div[style*="top: -50000px"][style*="width: 50000px"] {
left: -50000px !important;
}
`;
const style = document.createElement('style');
style.id = styleId;
style.textContent = cssText;
style.textContent = cssText + xtermMeasurementFix;
document.head.appendChild(style);
}