Refresh UI components and showcase; release updated framework packages

This commit is contained in:
Clintchiz committed 2026-09-20 18:13:24 +05:30
1 parent d800228585
commit c77f5e1cec
293 files changed
+22544 -35778

No files matched your search

+4
View File
@@ -1,5 +1,9 @@
# @wrnexus/viewer
Use `hideName={true}` on `<Viewer>` (or `{ hideName: true }` in `mountViewer`)
when the surrounding app already displays the filename. Defaults to false;
accessible labels, file-type detection and download filenames are preserved.
Complete implementation of the
[file viewer plan](../../docs/plans/2026-09-08-file-viewer-design.md).
+3 -2
View File
@@ -3,6 +3,7 @@ component Viewer {
props {
files: unknown[] = []
richPdf: boolean = false
hideName: boolean = false
fullScreen: boolean = false
layout: string = "inline"
src: string = ""
@@ -16,8 +17,8 @@ component Viewer {
class: string = ""
}
view {
<section class="wrn-viewer {class}" data-theme="{theme === 'auto' ? '' : theme}" data-wrnexus-runtime="viewer" data-files="{JSON.stringify(files)}" data-rich-pdf="{richPdf}" data-full-screen="{fullScreen}" data-layout="{layout}" data-src="{src}" data-key="{key}" data-store="{store}" data-name="{name}" data-mime="{mime}" data-size="{size}" aria-label="{name === '' ? 'File preview' : name}">
<ViewerShell name={name} />
<section class="wrn-viewer {class}" data-theme="{theme === 'auto' ? '' : theme}" data-wrnexus-runtime="viewer" data-files="{JSON.stringify(files)}" data-rich-pdf="{richPdf}" data-hide-name="{hideName}" data-full-screen="{fullScreen}" data-layout="{layout}" data-src="{src}" data-key="{key}" data-store="{store}" data-name="{name}" data-mime="{mime}" data-size="{size}" aria-label="{name === '' ? 'File preview' : name}">
<ViewerShell name={name} hideName={hideName} />
<noscript>Enable JavaScript to preview this file.</noscript>
</section>
}
+2 -1
View File
@@ -11,11 +11,12 @@ import ArchiveViewer from "@wrnexus/viewer/components/ArchiveViewer.wrn"
component ViewerShell {
props {
name: string = "File"
hideName: boolean = false
class: string = ""
}
view {
<div class="wrn-viewer-shell {class}">
<ViewerToolbar name={name} />
<ViewerToolbar name={name} hideName={hideName} />
<div class="wrn-viewer-content" data-viewer-content=""><p role="status">Loading preview…</p></div>
<template data-viewer-template="DownloadCard"><DownloadCard /></template>
<template data-viewer-template="ImageViewer"><ImageViewer /></template>
+2 -1
View File
@@ -1,11 +1,12 @@
component ViewerToolbar {
props {
name: string = "File"
hideName: boolean = false
class: string = ""
}
view {
<div class="wrn-viewer-toolbar {class}" data-viewer-toolbar="" role="toolbar" aria-label="File preview controls">
<span data-viewer-title="">{name}</span>
<span data-viewer-title="" hidden={hideName}>{name}</span>
<span data-viewer-controls=""></span>
<span data-viewer-actions=""></span>
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/viewer",
"version": "0.8.12",
"version": "0.8.13",
"type": "module",
"sideEffects": true,
"description": "Read-only file previews with bounded loading and graceful fallback.",
+7
View File
@@ -27,6 +27,8 @@ function formatBytes(bytes: number): string {
/** Mount a preview, including local File/Blob sources. Dispose before reusing the host. */
export interface MountViewerOptions {
/** Hide the visual filename while retaining accessible labels and download names. */
hideName?: boolean;
richPdf?: boolean;
/**
* Opens covering the page, with a Close control that returns it inline. This
@@ -83,6 +85,8 @@ export function mountViewer(
toolbar.querySelector<HTMLElement>("[data-viewer-title]") ??
element("span", source.name || "File");
title.textContent = source.name || "File";
title.hidden = options.hideName === true;
title.setAttribute('data-viewer-title','');
if (!toolbar.contains(title)) toolbar.append(title);
const controls = toolbar.querySelector<HTMLElement>("[data-viewer-controls]") ?? element("span");
const actions = toolbar.querySelector<HTMLElement>("[data-viewer-actions]") ?? element("span");
@@ -399,6 +403,7 @@ export function startViewerRuntime(root: Document = document): () => void {
const signature = [
host.dataset.files,
host.dataset.richPdf,
host.dataset.hideName,
host.dataset.fullScreen,
host.dataset.layout,
host.dataset.src,
@@ -425,6 +430,7 @@ export function startViewerRuntime(root: Document = document): () => void {
},
{
richPdf: host.dataset.richPdf === "true",
hideName: host.dataset.hideName === "true",
fullScreen: host.dataset.fullScreen === "true",
layout: host.dataset.layout === "page" ? "page" : "inline",
},
@@ -442,6 +448,7 @@ export function startViewerRuntime(root: Document = document): () => void {
attributes: true,
attributeFilter: [
"data-full-screen",
"data-hide-name",
"data-layout",
"data-files",
"data-rich-pdf",
@@ -0,0 +1,8 @@
import { test, expect } from "bun:test";
import manifest from "../package.json";
test("published viewer retains browser auto-mount side effects", () => {
// The runtime registers renderers and installs DOM/navigation listeners on import.
// Marking split published chunks side-effect-free drops those initializers.
expect(manifest.sideEffects).toBe(true);
});
+10
View File
@@ -12,6 +12,16 @@ function fixture() {
window.document.body.append(host);
return { window, host: host as unknown as HTMLElement };
}
test('hideName hides only the visual title and preserves the file identity',async()=>{
const {window,host}=fixture();
const dispose=mountViewer(host,{name:'report.txt',blob:new Blob(['hello'])},{hideName:true});
await settled();
expect(host.querySelector<HTMLElement>('[data-viewer-title]')?.hidden??host.querySelector<HTMLElement>('.wrn-viewer-toolbar span')?.hidden).toBe(true);
expect(host.getAttribute('aria-label')).toBe('report.txt');
expect(host.querySelector<HTMLAnchorElement>('a[download]')?.download).toBe('report.txt');
dispose();window.close();
});
test("image controls, failure fallback, and teardown", async () => {
const { window, host } = fixture();
const dispose = mountViewer(host, {
+5 -3
View File
@@ -27,6 +27,8 @@
align-items: center;
gap: 0.5rem;
}
.wrn-viewer-toolbar > [data-viewer-title][hidden] { display: none; }
.wrn-viewer-toolbar:has(> [data-viewer-title][hidden]) { justify-content: flex-end; }
/* The generic control look applies only to controls the package has not styled
itself; without this it outranks every specific rule below. */
.wrn-viewer button:not([class*="wrn-viewer-"]),
@@ -903,9 +905,9 @@
/* `Viewer.wrn` renders the toolbar and content inside a shell, so the growing
box is two levels down: every wrapper between has to flex for the content to
fill the space these modes hand it. */
.wrn-viewer-dialog .wrn-viewer > *:not(noscript),
.wrn-viewer-expanded > *:not(noscript),
.wrn-viewer-as-page > *:not(noscript),
.wrn-viewer-dialog .wrn-viewer > *:not(noscript, .wrn-viewer-toolbar, .wrn-viewer-content),
.wrn-viewer-expanded > *:not(noscript, .wrn-viewer-toolbar, .wrn-viewer-content),
.wrn-viewer-as-page > *:not(noscript, .wrn-viewer-toolbar, .wrn-viewer-content),
.wrn-viewer-dialog .wrn-viewer-shell,
.wrn-viewer-expanded .wrn-viewer-shell,
.wrn-viewer-as-page .wrn-viewer-shell {