npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zdenekgebauer/dosiero

v1.0.1

Published

file manager

Downloads

404

Readme

Dosiero

File manager for web applications. You can use it as stand-alone manager or as plugin for TinyMCE and CKEditor. Full integration requires server part. Usage and documentation

Installation

npm install @zdenekgebauer/dosiero

This half is the browser half. It renders the manager and enforces nothing: every limit it shows is there to save a pointless upload, and the server checks all of it again. The endpoint above has to be answered by a connector, and the reference one is PHP:

composer require zdenekgebauer/dosiero

Client and connector are released together and share one protocol version, so keep them on matching majors. The connector's README covers the entry point, access control and how to secure the data directory: zdenekgebauer/dosiero-php.

The package exports Dosiero, FileData and the ConfigInterface / TranslationInterface types from a single entry point; dist/ is an implementation detail and cannot be imported directly.

import {Dosiero} from "@zdenekgebauer/dosiero";

const manager = new Dosiero({
    mode: "iframe",
    iframe: document.getElementById("fm"),
    endpoint: "/dosiero/",
});

manager.open();

Constructing does not open anything. The constructor validates the configuration and returns; open() builds the manager's document, renders the UI and asks the server for the storages. It returns the instance, so new Dosiero({…}).open() is the usual one-liner. Calling it twice does nothing the second time, and destroy() tears everything down so the same instance can be opened again.

In mode: "iframe" the manager draws no Close button — it has no window to close, and pressing one would leave an empty rectangle on your page. The page that embeds it decides how the user backs out, and calls close() to do it: that empties the iframe, releases the listeners and invokes your onClose callback. Selecting a file does not close it either: onSelectFile receives the files and the manager stays open. In mode: "window" the button is there, because closing its own popup is something the manager can do, and the popup also closes after a file is selected.

The split matters in mode: "window": open() calls window.open(), which a browser only allows as a reaction to a user gesture. await import(…) inside the click handler no longer counts as one, so load the module up front and keep the handler synchronous:

const loading = import("@zdenekgebauer/dosiero"); // starts immediately, blocks nothing
let manager = null;

loading.then(({Dosiero}) => {
    manager = new Dosiero({mode: "window", endpoint: "/dosiero/"});
});

button.addEventListener("click", () => manager?.open()); // no await here

The shipped theme is @zdenekgebauer/dosiero/themes/default.min.css. When the library is bundled, its own location is no longer next to dist/; see Where the stylesheet lives.

From a CDN, without a build step

A bare package URL resolves to the UMD build, which defines a single global dosiero. The stylesheet is then fetched from the same CDN, next to the script, so no path is named twice:

<script src="https://cdn.jsdelivr.net/npm/@zdenekgebauer/dosiero"></script>
<script src="https://unpkg.com/@zdenekgebauer/dosiero"></script>
new dosiero.Dosiero({mode: "iframe", iframe: document.getElementById("fm"), endpoint: "/dosiero/"}).open();

For an ES module instead of the global, jsDelivr serves one from the same package:

<script type="module">
    import {Dosiero} from "https://cdn.jsdelivr.net/npm/@zdenekgebauer/dosiero/+esm";
</script>

Pin the version in production. Each of those URLs follows the newest release, so a major version would arrive unannounced — write @zdenekgebauer/[email protected] instead.

Languages

English is built in. The package also ships cs, de, eo, pl and sk as JSON files under langs/, and anything a language leaves out falls back to English — so a partial file is fine.

With a bundler, import the one you need. No request, and a typo is a compile error:

import cs from "@zdenekgebauer/dosiero/langs/cs.json";

new Dosiero({translations: cs /* … */}).open();

Without a build step, fetch it through the helper, which knows where the package's own files live:

const cs = await Dosiero.loadLanguage("cs"); // or a url of your own: "/i18n/mine.json"

new Dosiero({translations: cs /* … */}).open();

Await it before constructing. The configuration is read when the instance is built and open() has to stay synchronous, so there is no point in the manager's lifecycle where it could wait for a language. Each url is fetched once, however many instances ask for it.

translations is a plain object, so overriding a few labels is a spread — the same layering as theme → stylesheets → styles for appearance:

new Dosiero({translations: {...cs, buttonUpload: "Nahrát soubory"}}).open();

Switching language at runtime means destroy() and a new instance, because the options are read when the manager opens.

Talking to the connector

Every request carries the header X-Dosiero-Protocol, and a connector from 1.0 on refuses one without it. That is what stops a page on another site from reaching your endpoint: a browser will not put a custom header on a plain form submission, and a script that tries has to ask the server's permission first. The value is the protocol version, not a secret — the protection comes from the browser's rules, not from anything being hard to guess.

Nothing has to be configured for this. It matters only if the endpoint lives on another origin than the page, in which case the connector has to name that origin, and the client has to be told to send credentials with the request:

new Dosiero({
    endpoint: "https://files.example.org/dosiero/",
    withCredentials: true, // send cookies and basic auth to the other origin
    // …
}).open();

Leave withCredentials off for a same-origin endpoint, which is the recommended setup. Note that a cookie belongs to the origin that set it, so session authentication does not survive the move to another domain whatever this option says.

Upload limits

The server states, per storage, how large an uploaded file may be and which extensions it accepts. The manager shows both in the upload dialog and refuses a file that cannot pass before sending it, so the transfer is not wasted. Nothing has to be configured on this side; a connector that does not report the limits is treated as stating none.

This is convenience, not a security boundary — the server checks every upload again regardless.

Appearance

Dosiero renders into a document of its own — the contentDocument of the iframe you hand it, or a popup window. CSS written on the host page therefore does not reach it, which is the first thing that surprises people. Everything below is about getting your own rules into that document.

By default the manager follows the host operating system and the user's light/dark setting, so in most cases there is nothing to configure.

Which look, which colour scheme

new Dosiero({
    mode: "iframe",
    iframe: document.getElementById("fm"),
    endpoint: "/dosiero/",

    os: "auto", // 'auto' (default) | 'win' | 'mac' | 'linux'
    colorScheme: "auto", // 'auto' (default) | 'light' | 'dark'
}).open();

os picks the token set. auto reads the host system and falls back to the neutral linux set when it cannot tell.

colorScheme exists because prefers-color-scheme inside an iframe reports the operating system, not the host page. An application with its own dark-mode switch would otherwise disagree with the manager, so pass the scheme your page is currently in.

Both end up on the root element of the manager's document as data-os and data-color-scheme, so your own CSS can key off them.

Your own CSS

Two ways in, both inserted after the shipped theme so a rule of equal specificity wins without !important:

new Dosiero({
    // …
    stylesheets: ["/css/dosiero-corporate.css"], // urls, in order
    styles: `
        :root {
            --dsr-files-background-selected: #0b5fff;
            --dsr-icon-color: #0b5fff;
        }
        .dsr-file-modified { display: none; }
    `, // string or string[]
}).open();

stylesheets is loaded first, styles last — a file is the broad override, an inline snippet the targeted tweak on top of it.

Overriding a token is the cheap path and reaches everything derived from it, icons included: they are masks tinted by --dsr-icon-color, not images with a baked-in colour. Overriding a rule works too, at the specificity you see in the list below.

Note that the appearance options are per instance. Two managers on one page can look different, and neither can affect the other — they own separate documents.

Where the stylesheet lives

The shipped theme is dist/themes/default.min.css, resolved relative to the host page. If your dist/ is elsewhere, say so:

new Dosiero({assetsUrl: "/vendor/dosiero/"}).open();

To replace the theme outright — a different layout, not just a different palette — use theme:

new Dosiero({theme: "/css/my-dosiero.css"}).open();

styles and stylesheets still come after it.

CSS reference

Names of tokens and classes are public API. Both lists are generated from the sources by npm run docs:css, so they cannot go stale.

Internal tokens named --dsr-sys-* carry the per-system values that the public tokens read. They are not part of the contract and may change in any release.

Tokens (59)

Light values. Dark ones follow color-scheme where the value is a system colour keyword, and are listed in src/styles/00_variables.css where it is not.

| Token | Windows | macOS | Linux / neutral | | --- | --- | --- | --- | | --dsr-background | Canvas | Canvas | Canvas | | --dsr-border | 1px solid #e5e5e5 | 1px solid #dfe0e1 | 1px solid #e5e5e5 | | --dsr-button-border-radius | 0.25em | 0.3em | 0.25em | | --dsr-dialog-background | #f7f7f7 | rgba(240, 240, 240, 1) | #f7f7f7 | | --dsr-dialog-border-radius | 0.5em | 0.4em | 0.5em | | --dsr-dialog-footer-border-top | var(--dsr-border) | 0 | 0 | | --dsr-dialog-footer-height | 3em | 2em | 3em | | --dsr-dialog-header-background | #fff | rgba(255, 255, 255, 1) | #fff | | --dsr-dialog-input-background | Field | Field | Field | | --dsr-dialog-input-border | 1px solid #8a8a8a | 1px solid #8a8a8a | 1px solid #8a8a8a | | --dsr-dialog-input-color | FieldText | FieldText | FieldText | | --dsr-dialog-input-margin | 0 0 0 var(--dsr-space-small) | 0 | 0 0 0 var(--dsr-space-small) | | --dsr-dialog-input-width | auto | 100% | auto | | --dsr-dialog-overlay-background | rgba(0, 0, 0, 0.7) | rgba(0, 0, 0, 0.3) | rgba(0, 0, 0, 0.5) | | --dsr-dialog-text-color | CanvasText | CanvasText | CanvasText | | --dsr-files-background-hover | #e5f3ff | #e5f3ff | #ececec | | --dsr-files-background-selected | #cce8ff | #c3c3c3 | #d9d9d9 | | --dsr-files-stripe-background | transparent | #f4f5f5 | transparent | | --dsr-footer-background | #fff | #f5f6f7 | #f7f7f7 | | --dsr-footer-height | 2em | 2.5em | 2.2em | | --dsr-header-background | #fff | linear-gradient(#e6e6e6, #cfcfcf) | #f7f7f7 | | --dsr-header-button-background | inherit | inherit | transparent | | --dsr-header-button-background-hover | #ececec | #cce8ff | #ececec | | --dsr-header-button-width | 4.5em | 3em | 4em | | --dsr-header-buttons-border-right | var(--dsr-border) | 0 | 0 | | --dsr-header-buttons-height | 4.5em | 1.6em | 3.5em | | --dsr-header-height | 5em | 3em | 4em | | --dsr-header-icon-size | contain | 75% 75% | contain | | --dsr-header-icon-width | 3em | 100% | 2.4em | | --dsr-header-search-background | Field | #fafafa | Field | | --dsr-header-search-height | auto | 1.6em | auto | | --dsr-header-search-text-color | FieldText | FieldText | FieldText | | --dsr-icon-arrow | inlined arrow.svg | inlined arrow.svg | inlined arrow.svg | | --dsr-icon-color | CanvasText | CanvasText | CanvasText | | --dsr-icon-color-disabled | GrayText | GrayText | GrayText | | --dsr-icon-color-folder | #ffd971 | #54c1ec | #e8a33d | | --dsr-icon-copy | inlined copy.svg | inlined copy.svg | inlined copy.svg | | --dsr-icon-delete | inlined delete.svg | inlined delete.svg | inlined delete.svg | | --dsr-icon-file | inlined file.svg | inlined file.svg | inlined file.svg | | --dsr-icon-folder | inlined folder.svg | inlined folder.svg | inlined folder.svg | | --dsr-icon-list | inlined list.svg | inlined list.svg | inlined list.svg | | --dsr-icon-move | inlined move.svg | inlined move.svg | inlined move.svg | | --dsr-icon-new-folder | inlined new-folder.svg | inlined new-folder.svg | inlined new-folder.svg | | --dsr-icon-reload | inlined reload.svg | inlined reload.svg | inlined reload.svg | | --dsr-icon-rename | inlined rename.svg | inlined rename.svg | inlined rename.svg | | --dsr-icon-search | inlined search.svg | inlined search.svg | inlined search.svg | | --dsr-icon-tiles | inlined tiles.svg | inlined tiles.svg | inlined tiles.svg | | --dsr-icon-upload | inlined upload.svg | inlined upload.svg | inlined upload.svg | | --dsr-selection-border-radius | 0.25em | 0.3em | 0.25em | | --dsr-space | 0.5em | 0.5em | 0.5em | | --dsr-space-small | 0.3em | 0.3em | 0.3em | | --dsr-text-color | CanvasText | CanvasText | CanvasText | | --dsr-text-color-disabled | GrayText | GrayText | GrayText | | --dsr-text-color-selected | CanvasText | CanvasText | CanvasText | | --dsr-tree-background | #fff | linear-gradient(#e6e6e6, #cfcfcf) | Canvas | | --dsr-tree-background-hover | #ececec | inherit | #ececec | | --dsr-tree-background-selected | #d9d9d9 | #cce8ff | #d9d9d9 | | --dsr-tree-border-right | var(--dsr-border) | 0 | var(--dsr-border) | | --dsr-tree-bullet-size | 70% 70% | contain | contain |

Derived from a CSS system colour, so they follow the user agent and high-contrast mode: --dsr-background, --dsr-dialog-input-background, --dsr-dialog-input-color, --dsr-dialog-text-color, --dsr-header-search-background, --dsr-header-search-text-color, --dsr-icon-color, --dsr-icon-color-disabled, --dsr-text-color, --dsr-text-color-disabled, --dsr-text-color-selected, --dsr-tree-background.

Classes (47)

dsr-active · dsr-collapsed · dsr-dialog · dsr-dialog-body · dsr-dialog-confirm · dsr-dialog-copy · dsr-dialog-footer · dsr-dialog-header · dsr-dialog-mkdir · dsr-dialog-overlay · dsr-dialog-rename · dsr-dialog-upload · dsr-file · dsr-file-image · dsr-file-modified · dsr-file-name · dsr-file-size · dsr-files · dsr-files-list · dsr-files-tiles · dsr-folders · dsr-footer · dsr-footer-buttons · dsr-footer-statusbar · dsr-has-children · dsr-header · dsr-header-buttons · dsr-header-search · dsr-icon · dsr-icon-file · dsr-icon-folder · dsr-selected · dsr-statusbar-date · dsr-statusbar-item · dsr-statusbar-name · dsr-statusbar-size · dsr-tree-item · dsr-tree-item-bullet · dsr-tree-item-name · dsr-upload-limits · dsr-upload-name · dsr-upload-progress · dsr-upload-progress-bar · dsr-upload-progress-value · dsr-upload-rejected · dsr-upload-result · dsr-upload-size

How these names may change

| Change | Release | | ---------------------------------------------- | -------------------------------- | | Adding a token or a class | minor | | Adding a value to an existing option | minor | | Renaming a token or a class | major | | Changing what an existing token or class means | major | | Removing a token or a class | major | | Changing an internal --dsr-sys-* token | patch — not part of the contract |

Default values of tokens are not part of the contract: they track the operating systems Dosiero imitates, so a colour may be corrected in a patch release. Pin what you depend on by setting the token yourself.