@vespercms/admin 0.0.0-stage → 0.1.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -2
- package/dist/BodyEditor-_mkh0BSQ.js +3047 -0
- package/dist/a11y/FieldNotes.d.ts +23 -0
- package/dist/a11y/NotesDrawer.d.ts +23 -0
- package/dist/a11y/pdf.d.ts +3 -0
- package/dist/a11y/run.d.ts +3 -0
- package/dist/a11y/useNotes.d.ts +28 -0
- package/dist/app/App.d.ts +2 -0
- package/dist/app/Screen.d.ts +88 -0
- package/dist/app/UserMenu.d.ts +7 -0
- package/dist/app/api.d.ts +75 -0
- package/dist/app/appearance.d.ts +10 -0
- package/dist/app/browser-api.d.ts +17 -0
- package/dist/app/collection-icon.d.ts +12 -0
- package/dist/app/context.d.ts +23 -0
- package/dist/app/entries-changed.d.ts +11 -0
- package/dist/app/file-url.d.ts +13 -0
- package/dist/app/format.d.ts +4 -0
- package/dist/app/idb.d.ts +7 -0
- package/dist/app/load.d.ts +16 -0
- package/dist/app/media.d.ts +5 -0
- package/dist/app/navigation.d.ts +4 -0
- package/dist/app/pane-context.d.ts +19 -0
- package/dist/app/panes.d.ts +14 -0
- package/dist/app/router.d.ts +62 -0
- package/dist/body/BodyEditor.d.ts +42 -0
- package/dist/body/TableView.d.ts +2 -0
- package/dist/body/catalog.d.ts +47 -0
- package/dist/body/checks.d.ts +15 -0
- package/dist/body/chips.d.ts +31 -0
- package/dist/body/commands.d.ts +31 -0
- package/dist/body/document.d.ts +42 -0
- package/dist/body/insert.d.ts +69 -0
- package/dist/body/nodes.d.ts +38 -0
- package/dist/body/reveal.d.ts +13 -0
- package/dist/body/scroll.d.ts +16 -0
- package/dist/body/settings.d.ts +19 -0
- package/dist/body/shortcuts.d.ts +13 -0
- package/dist/body/table.d.ts +31 -0
- package/dist/body/ui.d.ts +30 -0
- package/dist/body/views.d.ts +9 -0
- package/dist/checks-C-0xDu-I.js +57 -0
- package/dist/cloud/CloudGate.d.ts +14 -0
- package/dist/cloud/access.d.ts +14 -0
- package/dist/cloud/changes.d.ts +26 -0
- package/dist/cloud/connection.d.ts +100 -0
- package/dist/cloud/editor.d.ts +44 -0
- package/dist/cloud/session.d.ts +62 -0
- package/dist/cloud/storage.d.ts +45 -0
- package/dist/editor/Field.d.ts +24 -0
- package/dist/editor/PagePicker.d.ts +20 -0
- package/dist/editor/arrange.d.ts +13 -0
- package/dist/editor/context.d.ts +23 -0
- package/dist/editor/copy.d.ts +9 -0
- package/dist/editor/reveal.d.ts +7 -0
- package/dist/editor/target.d.ts +21 -0
- package/dist/editor/values.d.ts +18 -0
- package/dist/geo-map-CT3OGD7O.js +110 -0
- package/dist/github/GitHubGate.d.ts +15 -0
- package/dist/github/api.d.ts +20 -0
- package/dist/github/cache.d.ts +2 -0
- package/dist/github/session.d.ts +35 -0
- package/dist/index.d.ts +3 -0
- package/dist/markdown-4fVeej3c.js +211 -0
- package/dist/media/MediaPicker.d.ts +28 -0
- package/dist/media/file-icon.d.ts +3 -0
- package/dist/media/formats.d.ts +23 -0
- package/dist/media/hygiene.d.ts +37 -0
- package/dist/media/upload.d.ts +31 -0
- package/dist/mount.d.ts +3 -0
- package/dist/pdf-m5s_uXJ3.js +24 -0
- package/dist/run-y4XRkg17.js +7 -0
- package/dist/schedule/ScheduleDialog.d.ts +28 -0
- package/dist/schedule/ScheduleSummary.d.ts +12 -0
- package/dist/schedule/ScheduledView.d.ts +8 -0
- package/dist/schedule/cloud.d.ts +21 -0
- package/dist/schedule/now.d.ts +5 -0
- package/dist/schedule/times.d.ts +35 -0
- package/dist/screens/EntryEditor.d.ts +13 -0
- package/dist/screens/EntryList.d.ts +16 -0
- package/dist/screens/Home.d.ts +6 -0
- package/dist/screens/ListPane.d.ts +6 -0
- package/dist/screens/MediaBrowser.d.ts +7 -0
- package/dist/screens/NotFound.d.ts +3 -0
- package/dist/screens/PageTree.d.ts +10 -0
- package/dist/screens/Redirects.d.ts +1 -0
- package/dist/screens/Unavailable.d.ts +8 -0
- package/dist/screens/status.d.ts +36 -0
- package/dist/shortcuts-CarKeDqp.js +4666 -0
- package/dist/studio.css +3 -0
- package/dist/studio.js +6449 -0
- package/dist/types.d.ts +76 -0
- package/dist/widgets/basic.d.ts +25 -0
- package/dist/widgets/blank-date.d.ts +8 -0
- package/dist/widgets/crops.d.ts +43 -0
- package/dist/widgets/focal-point.d.ts +15 -0
- package/dist/widgets/geo-map.d.ts +10 -0
- package/dist/widgets/geo.d.ts +16 -0
- package/dist/widgets/group.d.ts +41 -0
- package/dist/widgets/icon.d.ts +9 -0
- package/dist/widgets/image.d.ts +16 -0
- package/dist/widgets/markdown-lazy.d.ts +2 -0
- package/dist/widgets/markdown.d.ts +2 -0
- package/dist/widgets/reference.d.ts +12 -0
- package/dist/widgets/registry.d.ts +32 -0
- package/dist/widgets/special.d.ts +20 -0
- package/dist/widgets/union.d.ts +36 -0
- package/package.json +63 -4
- package/src/a11y/FieldNotes.tsx +56 -0
- package/src/a11y/NotesDrawer.tsx +81 -0
- package/src/a11y/pdf.ts +45 -0
- package/src/a11y/run.ts +12 -0
- package/src/a11y/useNotes.ts +139 -0
- package/src/app/App.tsx +156 -0
- package/src/app/Screen.tsx +416 -0
- package/src/app/UserMenu.tsx +95 -0
- package/src/app/api.ts +176 -0
- package/src/app/appearance.ts +54 -0
- package/src/app/browser-api.ts +173 -0
- package/src/app/collection-icon.ts +15 -0
- package/src/app/context.tsx +32 -0
- package/src/app/entries-changed.ts +48 -0
- package/src/app/file-url.tsx +44 -0
- package/src/app/format.ts +39 -0
- package/src/app/idb.ts +82 -0
- package/src/app/load.ts +40 -0
- package/src/app/media.ts +20 -0
- package/src/app/navigation.ts +4 -0
- package/src/app/pane-context.ts +25 -0
- package/src/app/panes.tsx +513 -0
- package/src/app/router.ts +158 -0
- package/src/body/BodyEditor.tsx +1139 -0
- package/src/body/TableView.tsx +700 -0
- package/src/body/catalog.ts +198 -0
- package/src/body/checks.ts +83 -0
- package/src/body/chips.ts +150 -0
- package/src/body/commands.ts +191 -0
- package/src/body/document.ts +388 -0
- package/src/body/insert.tsx +301 -0
- package/src/body/nodes.ts +287 -0
- package/src/body/reveal.ts +93 -0
- package/src/body/scroll.ts +53 -0
- package/src/body/settings.tsx +510 -0
- package/src/body/shortcuts.ts +31 -0
- package/src/body/table.ts +192 -0
- package/src/body/ui.ts +77 -0
- package/src/body/views.tsx +268 -0
- package/src/build.ts +6 -0
- package/src/cloud/CloudGate.tsx +245 -0
- package/src/cloud/access.ts +52 -0
- package/src/cloud/changes.tsx +474 -0
- package/src/cloud/connection.ts +281 -0
- package/src/cloud/editor.tsx +250 -0
- package/src/cloud/session.ts +289 -0
- package/src/cloud/storage.ts +212 -0
- package/src/editor/Field.tsx +182 -0
- package/src/editor/PagePicker.tsx +102 -0
- package/src/editor/arrange.ts +98 -0
- package/src/editor/context.tsx +31 -0
- package/src/editor/copy.ts +30 -0
- package/src/editor/reveal.ts +38 -0
- package/src/editor/target.ts +104 -0
- package/src/editor/values.ts +71 -0
- package/src/github/GitHubGate.tsx +298 -0
- package/src/github/api.ts +81 -0
- package/src/github/cache.ts +64 -0
- package/src/github/session.ts +146 -0
- package/src/index.ts +10 -0
- package/src/media/MediaPicker.tsx +302 -0
- package/src/media/file-icon.ts +17 -0
- package/src/media/formats.ts +262 -0
- package/src/media/hygiene.ts +220 -0
- package/src/media/upload.ts +47 -0
- package/src/mount.tsx +20 -0
- package/src/schedule/ScheduleDialog.tsx +303 -0
- package/src/schedule/ScheduleSummary.tsx +59 -0
- package/src/schedule/ScheduledView.tsx +294 -0
- package/src/schedule/cloud.ts +66 -0
- package/src/schedule/now.ts +18 -0
- package/src/schedule/times.ts +122 -0
- package/src/screens/EntryEditor.tsx +1817 -0
- package/src/screens/EntryList.tsx +272 -0
- package/src/screens/Home.tsx +131 -0
- package/src/screens/ListPane.tsx +477 -0
- package/src/screens/MediaBrowser.tsx +253 -0
- package/src/screens/NotFound.tsx +19 -0
- package/src/screens/PageTree.tsx +738 -0
- package/src/screens/Redirects.tsx +522 -0
- package/src/screens/Unavailable.tsx +27 -0
- package/src/screens/status.tsx +104 -0
- package/src/studio.css +168 -0
- package/src/types.ts +82 -0
- package/src/widgets/basic.tsx +315 -0
- package/src/widgets/blank-date.ts +32 -0
- package/src/widgets/crops.tsx +399 -0
- package/src/widgets/focal-point.tsx +240 -0
- package/src/widgets/geo-map.tsx +160 -0
- package/src/widgets/geo.tsx +170 -0
- package/src/widgets/group.tsx +827 -0
- package/src/widgets/icon.tsx +47 -0
- package/src/widgets/image.tsx +407 -0
- package/src/widgets/markdown-lazy.tsx +22 -0
- package/src/widgets/markdown.tsx +267 -0
- package/src/widgets/reference.tsx +196 -0
- package/src/widgets/registry.ts +104 -0
- package/src/widgets/special.tsx +290 -0
- package/src/widgets/union.tsx +233 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type BlobCache, type ChangeSet, type CloudClient, type CommitResult, type Draft, type StorageAdapter, type StorageEntry, type StoredFile } from '@vespercms/core';
|
|
2
|
+
export type CloudStorageOptions = {
|
|
3
|
+
client: CloudClient;
|
|
4
|
+
cache?: BlobCache;
|
|
5
|
+
/** How long a snapshot of the tree and drafts is trusted. */
|
|
6
|
+
snapshotTtlMs?: number;
|
|
7
|
+
/** Told whenever the drafts change, here or as the API reports them. */
|
|
8
|
+
onDrafts?: (drafts: readonly Draft[], version: string) => void;
|
|
9
|
+
now?: () => number;
|
|
10
|
+
};
|
|
11
|
+
export declare class CloudStorage implements StorageAdapter {
|
|
12
|
+
readonly kind: "cloud";
|
|
13
|
+
private readonly client;
|
|
14
|
+
private readonly cache;
|
|
15
|
+
private readonly ttl;
|
|
16
|
+
private readonly onDrafts;
|
|
17
|
+
private readonly now;
|
|
18
|
+
private snapshotting;
|
|
19
|
+
private current;
|
|
20
|
+
/** New files waiting for the next save (`ChangeSet.defer`): an upload goes out with its entry. */
|
|
21
|
+
private readonly pending;
|
|
22
|
+
private wanted;
|
|
23
|
+
private batch;
|
|
24
|
+
constructor(options: CloudStorageOptions);
|
|
25
|
+
/** Forgets the snapshot, so the next read asks the cloud again. */
|
|
26
|
+
invalidate(): void;
|
|
27
|
+
private snapshot;
|
|
28
|
+
/** The drafts as of the latest snapshot, asking the cloud when that's old. */
|
|
29
|
+
drafts(): Promise<{
|
|
30
|
+
drafts: Draft[];
|
|
31
|
+
version: string;
|
|
32
|
+
commit: string;
|
|
33
|
+
}>;
|
|
34
|
+
head(): Promise<{
|
|
35
|
+
version: string;
|
|
36
|
+
}>;
|
|
37
|
+
list(options?: {
|
|
38
|
+
prefix?: string;
|
|
39
|
+
}): Promise<StorageEntry[]>;
|
|
40
|
+
read(path: string): Promise<StoredFile | null>;
|
|
41
|
+
/** Bytes by version: the cache first, then batched requests to the API. */
|
|
42
|
+
private bytes;
|
|
43
|
+
private fetchWanted;
|
|
44
|
+
commit(changeSet: ChangeSet): Promise<CommitResult>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FieldModel } from '@vespercms/core';
|
|
2
|
+
import { type ValuePath } from './values.ts';
|
|
3
|
+
export type FieldProps = {
|
|
4
|
+
field: FieldModel;
|
|
5
|
+
/** Where the value lives in the entry (concrete indexes for array items). */
|
|
6
|
+
path: ValuePath;
|
|
7
|
+
/** Replaces the field's label, e.g. "Speaker 2" for an array item. */
|
|
8
|
+
label?: string;
|
|
9
|
+
/**
|
|
10
|
+
* A list item. An item of objects or images sits in its row's panel, which
|
|
11
|
+
* names and frames it, so its legend is for screen readers only. An item is
|
|
12
|
+
* never marked optional: it exists or it doesn't.
|
|
13
|
+
*/
|
|
14
|
+
item?: 'row' | 'inline';
|
|
15
|
+
/** A top-level object or list is a section, except where the place already has its own heading (Details). */
|
|
16
|
+
asSection?: boolean;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* One field: its label, help and error text, and the widget that edits it
|
|
20
|
+
* (spec §6.3.3: the editor renders the label, the widget the control).
|
|
21
|
+
* Group widgets (objects, lists, choices) render as a fieldset whose legend
|
|
22
|
+
* is the label. Errors are linked to the control with aria-describedby.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Field({ field, path, label, item, asSection }: FieldProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PageLink } from '@vespercms/core';
|
|
2
|
+
/** Every page with a URL on the site, loaded once per studio. Empty until it arrives, or if it fails. */
|
|
3
|
+
export declare function usePages(): PageLink[];
|
|
4
|
+
/** Pages as link suggestions: found by title, inserted as their address. */
|
|
5
|
+
export declare function pageSuggestions(pages: readonly PageLink[]): {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
detail: string;
|
|
9
|
+
}[];
|
|
10
|
+
/**
|
|
11
|
+
* "Choose a page…" beside a URL field: a dialog with a searchable list of
|
|
12
|
+
* pages. `absolute` puts the site's address in front, for fields whose
|
|
13
|
+
* schema accepts only full URLs.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ChoosePageButton({ onPick, absolute, fieldLabel, isDisabled, }: {
|
|
16
|
+
onPick: (url: string) => void;
|
|
17
|
+
absolute?: string;
|
|
18
|
+
fieldLabel: string;
|
|
19
|
+
isDisabled?: boolean;
|
|
20
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CollectionManifest, FieldModel } from '@vespercms/core';
|
|
2
|
+
export type ArrangedFields = {
|
|
3
|
+
title: FieldModel | undefined;
|
|
4
|
+
main: FieldModel[];
|
|
5
|
+
details: FieldModel[];
|
|
6
|
+
/** The collection's own tabs, in the order it declares them, each with its fields. */
|
|
7
|
+
tabs: {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
fields: FieldModel[];
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
export declare function arrangeFields(collection: CollectionManifest): ArrangedFields;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { A11yNote, CollectionManifest, DataObject, DataValue } from '@vespercms/core';
|
|
2
|
+
import type { ValuePath } from './values.ts';
|
|
3
|
+
/** What every field in the open entry shares. */
|
|
4
|
+
export type EditorState = {
|
|
5
|
+
collection: CollectionManifest;
|
|
6
|
+
values: DataObject;
|
|
7
|
+
setValue: (path: ValuePath, value: DataValue) => void;
|
|
8
|
+
/** Validation messages by `pathKey`, from the last save. */
|
|
9
|
+
issues: ReadonlyMap<string, readonly string[]>;
|
|
10
|
+
/** Accessibility notes by `pathKey` of the field they're shown under (spec §6.18). */
|
|
11
|
+
notes?: ReadonlyMap<string, readonly A11yNote[]>;
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
/** The entry hasn't been saved yet. */
|
|
14
|
+
isNew: boolean;
|
|
15
|
+
/** The entry's file, site-relative; for a new entry, where it will most likely be saved. */
|
|
16
|
+
entryPath: string;
|
|
17
|
+
/** The open entry's ID (`null` until a new entry is saved). */
|
|
18
|
+
entryId: string | null;
|
|
19
|
+
/** Prefix for field ids in a form that isn't the entry's own (a block's settings). */
|
|
20
|
+
idPrefix?: string;
|
|
21
|
+
};
|
|
22
|
+
export declare const EditorContext: import("react").Context<EditorState | null>;
|
|
23
|
+
export declare function useEditor(): EditorState;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CollectionManifest, DataObject } from '@vespercms/core';
|
|
2
|
+
/**
|
|
3
|
+
* A copy's starting values (Duplicate, spec §10.3): every value as it is,
|
|
4
|
+
* " (copy)" after the title, and the fields that hold the entry's own address
|
|
5
|
+
* left empty so the copy gets its own. Those are the slug widget's, which then
|
|
6
|
+
* follow the new title, and a top-level `slug`, which Astro takes as the
|
|
7
|
+
* entry's ID. Images are shared, so their focal points and crops come along.
|
|
8
|
+
*/
|
|
9
|
+
export declare function copiedValues(collection: CollectionManifest, values: DataObject): DataObject;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const REVEAL_EVENT = "vesper:reveal";
|
|
2
|
+
/**
|
|
3
|
+
* Focuses an element as soon as it's on screen (a field on another tab
|
|
4
|
+
* shows once its tab is chosen; one inside a closed list item, once the
|
|
5
|
+
* item opens), and scrolls it into view.
|
|
6
|
+
*/
|
|
7
|
+
export declare function focusWhenShown(id: string, tries?: number, asked?: boolean): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type DataValue, type FieldIssue, type FieldModel } from '@vespercms/core';
|
|
2
|
+
import { type ValuePath } from './values.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Where an issue is shown: the deepest field on screen along its path. An
|
|
5
|
+
* issue inside a raw field, or in a nested value no control shows, lands on
|
|
6
|
+
* the nearest field that is shown.
|
|
7
|
+
*/
|
|
8
|
+
export declare function issueTarget(fields: readonly FieldModel[], path: FieldIssue['path']): {
|
|
9
|
+
path: ValuePath;
|
|
10
|
+
focusId: string;
|
|
11
|
+
label: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Where an issue is, in words, for a list of notes away from the form:
|
|
15
|
+
* "Section 2 (Showcase) › Image, alt". Beside its field, the field's own
|
|
16
|
+
* label is enough, but in the notes drawer two images in blocks would both
|
|
17
|
+
* read "Image, alt". A list item is named by its place and, in blocks, its
|
|
18
|
+
* kind, which says the list's name too ("Section 2"), so the list's own
|
|
19
|
+
* label is left out before it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function issuePlace(fields: readonly FieldModel[], path: FieldIssue['path'], values: DataValue): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type DataObject, type DataValue, type FieldModel } from '@vespercms/core';
|
|
2
|
+
/** A concrete path into an entry's values: keys and array indexes. */
|
|
3
|
+
export type ValuePath = readonly (string | number)[];
|
|
4
|
+
export declare function getIn(root: DataValue, path: ValuePath): DataValue;
|
|
5
|
+
/**
|
|
6
|
+
* Returns a copy of `root` with the value at `path` replaced. `undefined`
|
|
7
|
+
* removes an object key (the file then loses that line) and leaves array
|
|
8
|
+
* items in place. Missing parents are created.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setIn(root: DataObject, path: ValuePath, value: DataValue): DataObject;
|
|
11
|
+
/** A DOM id for the control at `path`: stable, unique per entry, and safe in selectors. */
|
|
12
|
+
export declare function fieldId(path: ValuePath, prefix?: string): string;
|
|
13
|
+
/** Key for matching validation issues to fields. */
|
|
14
|
+
export declare function pathKey(path: ValuePath): string;
|
|
15
|
+
/** A field model's own key: the last segment of its path (`'[]'` for array items). */
|
|
16
|
+
export declare function keyOf(field: FieldModel): string;
|
|
17
|
+
/** The value a new entry starts with: each field's static default, where it has one. */
|
|
18
|
+
export declare function initialValues(fields: readonly FieldModel[] | null): DataObject;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useEffect as e, useRef as t, useState as n } from "react";
|
|
2
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
3
|
+
import "maplibre-gl/dist/maplibre-gl.css";
|
|
4
|
+
import { Map as a, Marker as o, setWorkerUrl as s } from "maplibre-gl";
|
|
5
|
+
import c from "maplibre-gl/dist/maplibre-gl-worker.mjs?url";
|
|
6
|
+
//#region src/widgets/geo-map.tsx
|
|
7
|
+
s(c);
|
|
8
|
+
var l = "https://tiles.openfreemap.org/styles/liberty", u = {
|
|
9
|
+
lat: 20,
|
|
10
|
+
lng: 0
|
|
11
|
+
};
|
|
12
|
+
function d({ label: s, point: c, center: d, zoom: p, disabled: m, onChange: h }) {
|
|
13
|
+
let g = t(null), _ = t(null), v = t(null), y = t(h), [b, x] = n(!1), [S, C] = n("");
|
|
14
|
+
return e(() => {
|
|
15
|
+
y.current = h;
|
|
16
|
+
}), e(() => {
|
|
17
|
+
if (!g.current) return;
|
|
18
|
+
let e = c ?? d ?? u, t;
|
|
19
|
+
try {
|
|
20
|
+
t = new a({
|
|
21
|
+
container: g.current,
|
|
22
|
+
style: l,
|
|
23
|
+
center: [e.lng, e.lat],
|
|
24
|
+
zoom: p ?? (c || d ? 13 : 1),
|
|
25
|
+
attributionControl: { compact: !0 },
|
|
26
|
+
keyboard: !1
|
|
27
|
+
});
|
|
28
|
+
} catch {
|
|
29
|
+
Promise.resolve().then(() => {
|
|
30
|
+
x(!0);
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
t.on("error", () => {
|
|
35
|
+
x(!0);
|
|
36
|
+
}), t.getCanvas().setAttribute("tabindex", "-1"), t.getCanvas().setAttribute("aria-hidden", "true"), _.current = t;
|
|
37
|
+
let n = document.createElement("button");
|
|
38
|
+
n.type = "button", n.className = "vesper-map-pin", n.setAttribute("aria-label", `${s} pin. Use the arrow keys to move it.`);
|
|
39
|
+
let r = new o({
|
|
40
|
+
element: n,
|
|
41
|
+
draggable: !m
|
|
42
|
+
}).setLngLat([e.lng, e.lat]).addTo(t);
|
|
43
|
+
v.current = r;
|
|
44
|
+
let i = (e, t) => {
|
|
45
|
+
let n = {
|
|
46
|
+
lat: f(e.lat),
|
|
47
|
+
lng: f(e.lng)
|
|
48
|
+
};
|
|
49
|
+
r.setLngLat([n.lng, n.lat]), y.current(n), t && C(`Latitude ${String(n.lat)}, longitude ${String(n.lng)}.`);
|
|
50
|
+
};
|
|
51
|
+
return r.on("dragend", () => {
|
|
52
|
+
let { lat: e, lng: t } = r.getLngLat();
|
|
53
|
+
i({
|
|
54
|
+
lat: e,
|
|
55
|
+
lng: t
|
|
56
|
+
}, !0);
|
|
57
|
+
}), t.on("click", (e) => {
|
|
58
|
+
m || i({
|
|
59
|
+
lat: e.lngLat.lat,
|
|
60
|
+
lng: e.lngLat.lng
|
|
61
|
+
}, !0);
|
|
62
|
+
}), n.addEventListener("keydown", (e) => {
|
|
63
|
+
let n = {
|
|
64
|
+
ArrowUp: [1, 0],
|
|
65
|
+
ArrowDown: [-1, 0],
|
|
66
|
+
ArrowRight: [0, 1],
|
|
67
|
+
ArrowLeft: [0, -1]
|
|
68
|
+
}[e.key];
|
|
69
|
+
if (!n || m) return;
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
let a = 360 / 2 ** t.getZoom() / 512 * 20 * (e.shiftKey ? 10 : 1), { lat: o, lng: s } = r.getLngLat(), c = {
|
|
72
|
+
lat: Math.max(-85, Math.min(85, o + n[0] * a)),
|
|
73
|
+
lng: (s + n[1] * a + 540) % 360 - 180
|
|
74
|
+
};
|
|
75
|
+
i(c, !0), t.panTo([c.lng, c.lat]);
|
|
76
|
+
}), () => {
|
|
77
|
+
r.remove(), t.remove(), _.current = null, v.current = null;
|
|
78
|
+
};
|
|
79
|
+
}, []), e(() => {
|
|
80
|
+
if (!c || !v.current) return;
|
|
81
|
+
let e = v.current.getLngLat();
|
|
82
|
+
(e.lat !== c.lat || e.lng !== c.lng) && (v.current.setLngLat([c.lng, c.lat]), _.current?.panTo([c.lng, c.lat]));
|
|
83
|
+
}, [c]), /* @__PURE__ */ i("div", {
|
|
84
|
+
className: "flex flex-col gap-2",
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ r("div", {
|
|
87
|
+
ref: g,
|
|
88
|
+
className: "h-64 w-full overflow-hidden rounded-md border border-border-strong"
|
|
89
|
+
}),
|
|
90
|
+
b && /* @__PURE__ */ r("p", {
|
|
91
|
+
className: "text-small text-content-muted",
|
|
92
|
+
children: "The map couldn’t load (it needs an internet connection). The number fields above work without it."
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ r("p", {
|
|
95
|
+
className: "text-small text-content-muted",
|
|
96
|
+
children: "Drag the pin, click the map, or focus the pin and use the arrow keys."
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ r("p", {
|
|
99
|
+
role: "status",
|
|
100
|
+
className: "sr-only",
|
|
101
|
+
children: S
|
|
102
|
+
})
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function f(e) {
|
|
107
|
+
return Math.round(e * 1e6) / 1e6;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { d as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StudioOptions } from '../types.ts';
|
|
2
|
+
import { type GitHubStudioSettings } from './session.ts';
|
|
3
|
+
type GateOptions = Omit<StudioOptions, 'api' | 'github' | 'account' | 'cloud'>;
|
|
4
|
+
export declare function GitHubGate({ settings, options, fetcher, }: {
|
|
5
|
+
settings: GitHubStudioSettings;
|
|
6
|
+
options: GateOptions;
|
|
7
|
+
fetcher?: typeof fetch;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* GitHub's picture at a size (`s`), or nothing when GitHub gave no address.
|
|
11
|
+
* It loads from GitHub's image host, which GitHub mode already trusts with
|
|
12
|
+
* every read and save (DECISIONS.md, "GitHub mode loads the editor's picture from GitHub").
|
|
13
|
+
*/
|
|
14
|
+
export declare function avatarAt(address: string, size: number): string;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type BlobCache, type Manifest, type UrlEntry } from '@vespercms/core';
|
|
2
|
+
import { type StudioApi } from '../app/api.ts';
|
|
3
|
+
import type { GitHubStudioSettings } from './session.ts';
|
|
4
|
+
export type GitHubApiOptions = {
|
|
5
|
+
settings: GitHubStudioSettings;
|
|
6
|
+
/** The current token. */
|
|
7
|
+
token: () => string;
|
|
8
|
+
manifest: Manifest;
|
|
9
|
+
loadMdxProps: () => Promise<Readonly<Record<string, unknown>>>;
|
|
10
|
+
loadUrlFunctions?: () => Promise<Readonly<Record<string, (entry: UrlEntry) => unknown>>>;
|
|
11
|
+
/** The site's static routes at build, for collision checks. */
|
|
12
|
+
routes?: readonly (readonly [string, string])[];
|
|
13
|
+
cache?: BlobCache;
|
|
14
|
+
/** Called when GitHub says the sign-in has expired. */
|
|
15
|
+
onExpired?: () => void;
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
};
|
|
18
|
+
export declare function createGitHubApi(options: GitHubApiOptions): StudioApi;
|
|
19
|
+
/** GitHub's errors as the studio shows them: plain words, and a status the screens understand. */
|
|
20
|
+
export declare function apiError(error: unknown, onExpired?: () => void): unknown;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** GitHub mode's settings, as the build writes them into the studio. */
|
|
2
|
+
export type GitHubStudioSettings = {
|
|
3
|
+
owner: string;
|
|
4
|
+
repo: string;
|
|
5
|
+
branch: string;
|
|
6
|
+
/** The site's folder inside the repository. */
|
|
7
|
+
root: string;
|
|
8
|
+
/** The OAuth relay's address. */
|
|
9
|
+
relay: string;
|
|
10
|
+
/** GitHub Enterprise Server's API, or a test double; GitHub's own when absent. */
|
|
11
|
+
apiUrl?: string;
|
|
12
|
+
/** The site's schemas built for the browser. */
|
|
13
|
+
schemas: string;
|
|
14
|
+
};
|
|
15
|
+
export type Session = {
|
|
16
|
+
token: string;
|
|
17
|
+
expiresAt: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function readSession(): Session | null;
|
|
20
|
+
export declare function signOut(): void;
|
|
21
|
+
/** Goes to the relay, then GitHub. `route` is the studio's hash route to come back to. */
|
|
22
|
+
export declare function startSignIn(settings: GitHubStudioSettings, route: string): Promise<void>;
|
|
23
|
+
export type Finished = {
|
|
24
|
+
ok: true;
|
|
25
|
+
session: Session;
|
|
26
|
+
route: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
message: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Finishes a sign-in when GitHub sent the browser back here, or returns
|
|
33
|
+
* null when it didn't. The code leaves the address either way.
|
|
34
|
+
*/
|
|
35
|
+
export declare function finishSignIn(settings: GitHubStudioSettings, fetcher?: typeof fetch): Promise<Finished | null>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { B as e, V as t, ht as n, n as r, x as i, z as a } from "./shortcuts-CarKeDqp.js";
|
|
2
|
+
import { LinkDialog as o, n as s, t as c } from "./BodyEditor-_mkh0BSQ.js";
|
|
3
|
+
import { useEffect as l, useRef as u, useState as d } from "react";
|
|
4
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
5
|
+
import { Extension as h } from "@tiptap/core";
|
|
6
|
+
import { Markdown as g } from "@tiptap/markdown";
|
|
7
|
+
import { EditorContent as _, useEditor as v, useEditorState as y } from "@tiptap/react";
|
|
8
|
+
import b from "@tiptap/starter-kit";
|
|
9
|
+
//#region src/widgets/markdown.tsx
|
|
10
|
+
var x = /* @__PURE__ */ new Set([
|
|
11
|
+
"doc",
|
|
12
|
+
"paragraph",
|
|
13
|
+
"text",
|
|
14
|
+
"hardBreak",
|
|
15
|
+
"bulletList",
|
|
16
|
+
"orderedList",
|
|
17
|
+
"listItem"
|
|
18
|
+
]), S = /* @__PURE__ */ new Set([
|
|
19
|
+
"doc",
|
|
20
|
+
"paragraph",
|
|
21
|
+
"text",
|
|
22
|
+
"hardBreak"
|
|
23
|
+
]);
|
|
24
|
+
function C(e, t = /* @__PURE__ */ new Set()) {
|
|
25
|
+
e.type && t.add(e.type);
|
|
26
|
+
for (let n of e.content ?? []) C(n, t);
|
|
27
|
+
return t;
|
|
28
|
+
}
|
|
29
|
+
var w = /* @__PURE__ */ new Set([
|
|
30
|
+
"space",
|
|
31
|
+
"paragraph",
|
|
32
|
+
"text",
|
|
33
|
+
"escape",
|
|
34
|
+
"strong",
|
|
35
|
+
"em",
|
|
36
|
+
"codespan",
|
|
37
|
+
"link",
|
|
38
|
+
"br"
|
|
39
|
+
]), T = /* @__PURE__ */ new Set([
|
|
40
|
+
...w,
|
|
41
|
+
"list",
|
|
42
|
+
"list_item"
|
|
43
|
+
]), E = /\[\^|&(?!(?:lt|gt|quot|amp);)[A-Za-z][A-Za-z0-9]*;/;
|
|
44
|
+
function D(e, t) {
|
|
45
|
+
return e.every((e) => t.has(e.type ?? "") && !(e.type === "text" && !e.tokens && E.test(e.raw ?? "")) && D(e.tokens ?? [], t) && D(e.items ?? [], t));
|
|
46
|
+
}
|
|
47
|
+
function O(e, t, n) {
|
|
48
|
+
try {
|
|
49
|
+
if (!D(e.instance.lexer(t), n ? w : T)) return !1;
|
|
50
|
+
let r = e.parse(t), i = n ? S : x;
|
|
51
|
+
if (![...C(r)].every((e) => i.has(e)) || n && (r.content?.length ?? 0) > 1) return !1;
|
|
52
|
+
let a = e.parse(e.serialize(r));
|
|
53
|
+
return JSON.stringify(a) === JSON.stringify(r);
|
|
54
|
+
} catch {
|
|
55
|
+
return !1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function k(x) {
|
|
59
|
+
let { value: S, onChange: C, field: w, id: T, describedBy: E, invalid: D, disabled: k } = x, A = w.widgetOptions?.inline === !0, [j, M] = d(!1), [N, P] = d(!1), F = u(C);
|
|
60
|
+
l(() => {
|
|
61
|
+
F.current = C;
|
|
62
|
+
});
|
|
63
|
+
let I = u(null), [L] = d(() => h.create({
|
|
64
|
+
name: "vesperCompactKeys",
|
|
65
|
+
addKeyboardShortcuts() {
|
|
66
|
+
return {
|
|
67
|
+
"Mod-k": () => (P(!0), !0),
|
|
68
|
+
Escape: () => (I.current?.querySelector("button")?.focus(), !0),
|
|
69
|
+
...A ? {
|
|
70
|
+
Enter: () => !0,
|
|
71
|
+
"Shift-Enter": () => !0
|
|
72
|
+
} : {}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
})), R = v({
|
|
76
|
+
immediatelyRender: !0,
|
|
77
|
+
shouldRerenderOnTransaction: !1,
|
|
78
|
+
editable: !k,
|
|
79
|
+
extensions: [
|
|
80
|
+
b.configure({
|
|
81
|
+
heading: !1,
|
|
82
|
+
blockquote: !1,
|
|
83
|
+
codeBlock: !1,
|
|
84
|
+
horizontalRule: !1,
|
|
85
|
+
strike: !1,
|
|
86
|
+
underline: !1,
|
|
87
|
+
trailingNode: !1,
|
|
88
|
+
...A ? {
|
|
89
|
+
bulletList: !1,
|
|
90
|
+
orderedList: !1,
|
|
91
|
+
listItem: !1,
|
|
92
|
+
listKeymap: !1
|
|
93
|
+
} : {},
|
|
94
|
+
link: {
|
|
95
|
+
openOnClick: !1,
|
|
96
|
+
autolink: !0,
|
|
97
|
+
defaultProtocol: "https"
|
|
98
|
+
}
|
|
99
|
+
}),
|
|
100
|
+
g,
|
|
101
|
+
L
|
|
102
|
+
],
|
|
103
|
+
editorProps: {
|
|
104
|
+
handleScrollToSelection: (e) => c(e, I.current),
|
|
105
|
+
attributes: {
|
|
106
|
+
id: T,
|
|
107
|
+
role: "textbox",
|
|
108
|
+
"aria-multiline": A ? "false" : "true",
|
|
109
|
+
"aria-labelledby": `${T}-label`,
|
|
110
|
+
"aria-describedby": [E, `${T}-md`].filter(Boolean).join(" "),
|
|
111
|
+
...D ? { "aria-invalid": "true" } : {},
|
|
112
|
+
...w.required ? { "aria-required": "true" } : {},
|
|
113
|
+
class: n("vesper-body w-full px-3 py-2 text-body text-content outline-none", A ? "min-h-10" : "min-h-24")
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
onCreate: ({ editor: e }) => {
|
|
117
|
+
let t = typeof S == "string" ? S : "", n = e.markdown;
|
|
118
|
+
t !== "" && !O(n, t, A) ? M(!0) : e.commands.setContent(n.parse(t), { emitUpdate: !1 });
|
|
119
|
+
},
|
|
120
|
+
onUpdate: ({ editor: e }) => {
|
|
121
|
+
let t = e.getMarkdown().trim();
|
|
122
|
+
F.current(t === "" && !w.required ? void 0 : t);
|
|
123
|
+
}
|
|
124
|
+
}), z = y({
|
|
125
|
+
editor: R,
|
|
126
|
+
selector: ({ editor: e }) => ({
|
|
127
|
+
bold: e.isActive("bold"),
|
|
128
|
+
italic: e.isActive("italic"),
|
|
129
|
+
code: e.isActive("code"),
|
|
130
|
+
link: e.isActive("link")
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
return j ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("textarea", {
|
|
134
|
+
id: T,
|
|
135
|
+
rows: A ? 2 : 5,
|
|
136
|
+
value: typeof S == "string" ? S : "",
|
|
137
|
+
onChange: (e) => {
|
|
138
|
+
C(e.target.value === "" && !w.required ? void 0 : e.target.value);
|
|
139
|
+
},
|
|
140
|
+
"aria-describedby": [E, `${T}-md`].filter(Boolean).join(" "),
|
|
141
|
+
"aria-invalid": D || void 0,
|
|
142
|
+
"aria-required": w.required || void 0,
|
|
143
|
+
disabled: k,
|
|
144
|
+
className: n(i, "resize-y font-mono text-small")
|
|
145
|
+
}), /* @__PURE__ */ p("p", {
|
|
146
|
+
id: `${T}-md`,
|
|
147
|
+
className: "text-small text-content-muted",
|
|
148
|
+
children: "This uses Markdown the compact editor can’t show, so it’s edited as text."
|
|
149
|
+
})] }) : /* @__PURE__ */ m("div", {
|
|
150
|
+
className: "flex flex-col gap-2",
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ m("div", {
|
|
153
|
+
className: n("rounded-md border border-border-strong bg-surface-raised", D && "border-danger", k && "opacity-60", "has-[.ProseMirror-focused]:outline-2 has-[.ProseMirror-focused]:outline-solid has-[.ProseMirror-focused]:outline-offset-2 has-[.ProseMirror-focused]:outline-focus", "has-[.ProseMirror-focused]:ring-6 has-[.ProseMirror-focused]:ring-(color:--glow-focus)"),
|
|
154
|
+
children: [/* @__PURE__ */ p("div", {
|
|
155
|
+
ref: I,
|
|
156
|
+
className: s,
|
|
157
|
+
children: /* @__PURE__ */ m(t, {
|
|
158
|
+
label: `Formatting for ${w.label}`,
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ p(a, {
|
|
161
|
+
icon: "bold",
|
|
162
|
+
label: "Bold",
|
|
163
|
+
shortcut: r("Mod", "B"),
|
|
164
|
+
isDisabled: k,
|
|
165
|
+
isSelected: z.bold,
|
|
166
|
+
onChange: () => R.chain().focus().toggleBold().run()
|
|
167
|
+
}),
|
|
168
|
+
/* @__PURE__ */ p(a, {
|
|
169
|
+
icon: "italic",
|
|
170
|
+
label: "Italic",
|
|
171
|
+
shortcut: r("Mod", "I"),
|
|
172
|
+
isDisabled: k,
|
|
173
|
+
isSelected: z.italic,
|
|
174
|
+
onChange: () => R.chain().focus().toggleItalic().run()
|
|
175
|
+
}),
|
|
176
|
+
/* @__PURE__ */ p(a, {
|
|
177
|
+
icon: "code",
|
|
178
|
+
label: "Code",
|
|
179
|
+
shortcut: r("Mod", "E"),
|
|
180
|
+
isDisabled: k,
|
|
181
|
+
isSelected: z.code,
|
|
182
|
+
onChange: () => R.chain().focus().toggleCode().run()
|
|
183
|
+
}),
|
|
184
|
+
/* @__PURE__ */ p(e, {
|
|
185
|
+
icon: "link",
|
|
186
|
+
label: z.link ? "Edit link" : "Link",
|
|
187
|
+
shortcut: r("Mod", "K"),
|
|
188
|
+
isDisabled: k,
|
|
189
|
+
onPress: () => {
|
|
190
|
+
P(!0);
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
})
|
|
195
|
+
}), /* @__PURE__ */ p(_, { editor: R })]
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ p("p", {
|
|
198
|
+
id: `${T}-md`,
|
|
199
|
+
className: "text-small text-content-muted",
|
|
200
|
+
children: "Saved as Markdown. Escape moves to the formatting buttons."
|
|
201
|
+
}),
|
|
202
|
+
/* @__PURE__ */ p(o, {
|
|
203
|
+
editor: R,
|
|
204
|
+
isOpen: N,
|
|
205
|
+
onOpenChange: P
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
export { k as MarkdownWidget };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type UploadStep } from './upload.ts';
|
|
2
|
+
export type MediaPickerProps = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
kind: 'image' | 'file';
|
|
6
|
+
/** File extensions a file field accepts, lower case without dots. Empty for any. */
|
|
7
|
+
extensions?: readonly string[];
|
|
8
|
+
/** Where a new upload goes, site-relative. */
|
|
9
|
+
folder: string;
|
|
10
|
+
/** Called with the chosen file's site-relative path, and what hygiene did to a new upload. */
|
|
11
|
+
onChoose: (path: string, notes?: readonly string[]) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Choose a file: upload a new one (after hygiene and a duplicate check) or
|
|
15
|
+
* pick one already in the site. One dialog, two plain sections.
|
|
16
|
+
*/
|
|
17
|
+
export declare function MediaPicker({ isOpen, onOpenChange, kind, extensions, folder, onChoose, }: MediaPickerProps): import("react").JSX.Element;
|
|
18
|
+
export declare function UploadStatus({ step, onUpload, onReuse, onCancel, }: {
|
|
19
|
+
step: UploadStep | {
|
|
20
|
+
step: 'working';
|
|
21
|
+
text: string;
|
|
22
|
+
} | null;
|
|
23
|
+
onUpload: (ready: Extract<UploadStep, {
|
|
24
|
+
upload: unknown;
|
|
25
|
+
}>) => void;
|
|
26
|
+
onReuse: (path: string) => void;
|
|
27
|
+
onCancel: () => void;
|
|
28
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type DetectedType = 'jpeg' | 'png' | 'gif' | 'webp' | 'avif' | 'heic' | 'svg' | 'pdf' | 'other';
|
|
2
|
+
/** What a file is, from its first bytes rather than its name. */
|
|
3
|
+
export declare function detectType(bytes: Uint8Array): DetectedType;
|
|
4
|
+
export type ImageFacts = {
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
/** EXIF orientation, 1–8. 1 (or absent) means the pixels are already upright. */
|
|
8
|
+
orientation?: number;
|
|
9
|
+
/** EXIF GPS data is present. */
|
|
10
|
+
hasLocation: boolean;
|
|
11
|
+
/** Any metadata a lossless strip would remove. */
|
|
12
|
+
hasMetadata: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function jpegFacts(bytes: Uint8Array): ImageFacts;
|
|
15
|
+
/** The same JPEG without EXIF, XMP, IPTC or comments. The image data is untouched. */
|
|
16
|
+
export declare function stripJpeg(bytes: Uint8Array): Uint8Array;
|
|
17
|
+
export declare function pngFacts(bytes: Uint8Array): ImageFacts;
|
|
18
|
+
/** The same PNG without text, time or EXIF chunks. */
|
|
19
|
+
export declare function stripPng(bytes: Uint8Array): Uint8Array;
|
|
20
|
+
export declare function webpFacts(bytes: Uint8Array): ImageFacts;
|
|
21
|
+
/** The same WebP without EXIF or XMP, with the header flags and size fixed up. */
|
|
22
|
+
export declare function stripWebp(bytes: Uint8Array): Uint8Array;
|
|
23
|
+
export declare function imageFacts(type: DetectedType, bytes: Uint8Array): ImageFacts;
|