@orz/ui-hex 0.0.0-stage → 0.0.0
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/LICENSE +29 -0
- package/README.md +536 -2
- package/dist/column-fit.d.ts +16 -0
- package/dist/column-fit.d.ts.map +1 -0
- package/dist/column-fit.js +22 -0
- package/dist/column-fit.js.map +1 -0
- package/dist/decode.d.ts +11 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +63 -0
- package/dist/decode.js.map +1 -0
- package/dist/document.d.ts +75 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +523 -0
- package/dist/document.js.map +1 -0
- package/dist/editing.d.ts +66 -0
- package/dist/editing.d.ts.map +1 -0
- package/dist/editing.js +202 -0
- package/dist/editing.js.map +1 -0
- package/dist/encoding-picker.d.ts +6 -0
- package/dist/encoding-picker.d.ts.map +1 -0
- package/dist/encoding-picker.js +84 -0
- package/dist/encoding-picker.js.map +1 -0
- package/dist/encodings.d.ts +19 -0
- package/dist/encodings.d.ts.map +1 -0
- package/dist/encodings.js +106 -0
- package/dist/encodings.js.map +1 -0
- package/dist/format.d.ts +29 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +73 -0
- package/dist/format.js.map +1 -0
- package/dist/hex-editor.d.ts +27 -0
- package/dist/hex-editor.d.ts.map +1 -0
- package/dist/hex-editor.js +270 -0
- package/dist/hex-editor.js.map +1 -0
- package/dist/hex-grid.d.ts +102 -0
- package/dist/hex-grid.d.ts.map +1 -0
- package/dist/hex-grid.js +860 -0
- package/dist/hex-grid.js.map +1 -0
- package/dist/hex-inspector.d.ts +12 -0
- package/dist/hex-inspector.d.ts.map +1 -0
- package/dist/hex-inspector.js +68 -0
- package/dist/hex-inspector.js.map +1 -0
- package/dist/hex-paste.d.ts +26 -0
- package/dist/hex-paste.d.ts.map +1 -0
- package/dist/hex-paste.js +42 -0
- package/dist/hex-paste.js.map +1 -0
- package/dist/hex-view.d.ts +4 -0
- package/dist/hex-view.d.ts.map +1 -0
- package/dist/hex-view.js +6 -0
- package/dist/hex-view.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation.d.ts +25 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +100 -0
- package/dist/navigation.js.map +1 -0
- package/dist/page-cache.d.ts +45 -0
- package/dist/page-cache.d.ts.map +1 -0
- package/dist/page-cache.js +334 -0
- package/dist/page-cache.js.map +1 -0
- package/dist/source.d.ts +22 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +31 -0
- package/dist/source.js.map +1 -0
- package/dist/text-decoding-multibyte.d.ts +17 -0
- package/dist/text-decoding-multibyte.d.ts.map +1 -0
- package/dist/text-decoding-multibyte.js +448 -0
- package/dist/text-decoding-multibyte.js.map +1 -0
- package/dist/text-decoding.d.ts +45 -0
- package/dist/text-decoding.d.ts.map +1 -0
- package/dist/text-decoding.js +106 -0
- package/dist/text-decoding.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/use-hex-document-state.d.ts +10 -0
- package/dist/use-hex-document-state.d.ts.map +1 -0
- package/dist/use-hex-document-state.js +22 -0
- package/dist/use-hex-document-state.js.map +1 -0
- package/package.json +53 -3
- package/styles.css +456 -0
package/dist/decode.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// DataView.getFloat16 is too new to rely on in every consumer's target.
|
|
2
|
+
export function float16(bits) {
|
|
3
|
+
const sign = bits & 0x8000 ? -1 : 1;
|
|
4
|
+
const exponent = (bits >> 10) & 0x1f;
|
|
5
|
+
const mantissa = bits & 0x3ff;
|
|
6
|
+
if (exponent === 0)
|
|
7
|
+
return sign * mantissa * 2 ** -24;
|
|
8
|
+
if (exponent === 0x1f)
|
|
9
|
+
return mantissa ? Number.NaN : sign * Infinity;
|
|
10
|
+
return sign * (1 + mantissa / 1024) * 2 ** (exponent - 15);
|
|
11
|
+
}
|
|
12
|
+
function formatFloat(value) {
|
|
13
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
14
|
+
}
|
|
15
|
+
const ROWS = [
|
|
16
|
+
{
|
|
17
|
+
label: "Binary",
|
|
18
|
+
width: 1,
|
|
19
|
+
read: (v) => v.getUint8(0).toString(2).padStart(8, "0"),
|
|
20
|
+
},
|
|
21
|
+
{ label: "Int8", width: 1, read: (v) => String(v.getInt8(0)) },
|
|
22
|
+
{ label: "Uint8", width: 1, read: (v) => String(v.getUint8(0)) },
|
|
23
|
+
{ label: "Int16", width: 2, read: (v, le) => String(v.getInt16(0, le)) },
|
|
24
|
+
{ label: "Uint16", width: 2, read: (v, le) => String(v.getUint16(0, le)) },
|
|
25
|
+
{ label: "Int32", width: 4, read: (v, le) => String(v.getInt32(0, le)) },
|
|
26
|
+
{ label: "Uint32", width: 4, read: (v, le) => String(v.getUint32(0, le)) },
|
|
27
|
+
{
|
|
28
|
+
label: "Int64",
|
|
29
|
+
width: 8,
|
|
30
|
+
read: (v, le) => v.getBigInt64(0, le).toString(),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "Uint64",
|
|
34
|
+
width: 8,
|
|
35
|
+
read: (v, le) => v.getBigUint64(0, le).toString(),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: "Float16",
|
|
39
|
+
width: 2,
|
|
40
|
+
read: (v, le) => formatFloat(float16(v.getUint16(0, le))),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: "Float32",
|
|
44
|
+
width: 4,
|
|
45
|
+
read: (v, le) => formatFloat(v.getFloat32(0, le)),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: "Float64",
|
|
49
|
+
width: 8,
|
|
50
|
+
read: (v, le) => formatFloat(v.getFloat64(0, le)),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
/** One entry per inspector row, read from the first `available` bytes of `bytes`. */
|
|
54
|
+
export function decodeAll(bytes, available, littleEndian) {
|
|
55
|
+
const n = Math.min(available, bytes.length);
|
|
56
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, n);
|
|
57
|
+
return ROWS.map(({ label, width, read }) => ({
|
|
58
|
+
label,
|
|
59
|
+
width,
|
|
60
|
+
value: n >= width ? read(view, littleEndian) : null,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=decode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode.js","sourceRoot":"","sources":["../src/decode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ByteSource } from "./source.ts";
|
|
2
|
+
import type { HexSelection } from "./types.ts";
|
|
3
|
+
export interface HexDocumentOptions {
|
|
4
|
+
/** false locks the size: no insert mode, no delete or cut, no typing or pasting past the end. Default true. */
|
|
5
|
+
resizable?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export type HexPiece =
|
|
8
|
+
/** A range of the original source. */
|
|
9
|
+
{
|
|
10
|
+
kind: "source";
|
|
11
|
+
offset: number;
|
|
12
|
+
length: number;
|
|
13
|
+
}
|
|
14
|
+
/** Bytes that came from editing; a view onto the document's own buffer. */
|
|
15
|
+
| {
|
|
16
|
+
kind: "bytes";
|
|
17
|
+
bytes: Uint8Array;
|
|
18
|
+
};
|
|
19
|
+
export interface HexDocument extends ByteSource {
|
|
20
|
+
readonly source: ByteSource;
|
|
21
|
+
readonly resizable: boolean;
|
|
22
|
+
/** The undo position differs from the one `markSaved()` recorded. */
|
|
23
|
+
readonly dirty: boolean;
|
|
24
|
+
readonly canUndo: boolean;
|
|
25
|
+
readonly canRedo: boolean;
|
|
26
|
+
peek(offset: number, length: number): Uint8Array | undefined;
|
|
27
|
+
subscribe(onChange: () => void): () => void;
|
|
28
|
+
/** Replace `length` bytes at `offset` with `bytes`; insert is length 0, delete is empty bytes. */
|
|
29
|
+
replace(offset: number, length: number, bytes: Uint8Array): void;
|
|
30
|
+
insert(offset: number, bytes: Uint8Array): void;
|
|
31
|
+
/** Writes over the bytes from `offset`; past the end it extends the document, or stops there when the size is locked. */
|
|
32
|
+
overwrite(offset: number, bytes: Uint8Array): void;
|
|
33
|
+
delete(offset: number, length: number): void;
|
|
34
|
+
undo(): void;
|
|
35
|
+
redo(): void;
|
|
36
|
+
markSaved(): void;
|
|
37
|
+
/** Ranges in [offset, offset + length) whose bytes came from editing, as [start, end) pairs. */
|
|
38
|
+
modifiedRanges(offset: number, length: number): Array<[number, number]>;
|
|
39
|
+
pieces(): HexPiece[];
|
|
40
|
+
toBlob(type?: string): Promise<Blob>;
|
|
41
|
+
stream(): ReadableStream<Uint8Array>;
|
|
42
|
+
}
|
|
43
|
+
type EditKey = "type-hex" | "type-text";
|
|
44
|
+
export type HexDocumentChange = {
|
|
45
|
+
kind: "edit";
|
|
46
|
+
}
|
|
47
|
+
/** Saved, or the source changed underneath: nothing to restore. */
|
|
48
|
+
| {
|
|
49
|
+
kind: "other";
|
|
50
|
+
} | {
|
|
51
|
+
kind: "undo" | "redo";
|
|
52
|
+
selection: HexSelection | null | undefined;
|
|
53
|
+
};
|
|
54
|
+
export interface DocumentEdit {
|
|
55
|
+
offset: number;
|
|
56
|
+
length: number;
|
|
57
|
+
bytes: Uint8Array;
|
|
58
|
+
key?: EditKey;
|
|
59
|
+
before?: HexSelection | null;
|
|
60
|
+
after?: HexSelection | null;
|
|
61
|
+
}
|
|
62
|
+
export declare function createHexDocument(source: ByteSource, { resizable }?: HexDocumentOptions): HexDocument;
|
|
63
|
+
export declare function isHexDocument(value: unknown): value is HexDocument;
|
|
64
|
+
/** An edit carrying the editor's selections and a coalescing key; `replace` without them. */
|
|
65
|
+
export declare function editDocument(doc: HexDocument, edit: DocumentEdit): void;
|
|
66
|
+
/** Runs several edits as one undo step, with one notification. */
|
|
67
|
+
export declare function editGroup(doc: HexDocument, fn: () => void): void;
|
|
68
|
+
/** The next edit starts a new undo step even when it would continue the run. */
|
|
69
|
+
export declare function breakRun(doc: HexDocument): void;
|
|
70
|
+
/** What the most recent notification was for; undo and redo carry the selection to restore. */
|
|
71
|
+
export declare function lastChange(doc: HexDocument): HexDocumentChange;
|
|
72
|
+
/** Bumped by every change the document notifies. */
|
|
73
|
+
export declare function documentVersion(doc: HexDocument): number;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { createPageCache } from "./page-cache.js";
|
|
2
|
+
// Also the brand: a document is whatever createHexDocument registered here.
|
|
3
|
+
const internals = new WeakMap();
|
|
4
|
+
const ADD_CHUNK = 64 * 1024;
|
|
5
|
+
const EXPORT_CHUNK = 1 << 20;
|
|
6
|
+
function abortError(signal) {
|
|
7
|
+
return signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
8
|
+
}
|
|
9
|
+
/** The buffer edits append to. Bytes once written are never rewritten, so pieces, undo steps and exports share it. */
|
|
10
|
+
function createAddBuffer() {
|
|
11
|
+
const chunks = [];
|
|
12
|
+
const starts = [];
|
|
13
|
+
let fill = 0;
|
|
14
|
+
let end = 0;
|
|
15
|
+
const chunkAt = (at) => {
|
|
16
|
+
let lo = 0;
|
|
17
|
+
let hi = starts.length - 1;
|
|
18
|
+
while (lo < hi) {
|
|
19
|
+
const mid = (lo + hi + 1) >> 1;
|
|
20
|
+
if (starts[mid] <= at)
|
|
21
|
+
lo = mid;
|
|
22
|
+
else
|
|
23
|
+
hi = mid - 1;
|
|
24
|
+
}
|
|
25
|
+
return lo;
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
/** Where `bytes` now live; always inside one chunk. */
|
|
29
|
+
append(bytes) {
|
|
30
|
+
let chunk = chunks.at(-1);
|
|
31
|
+
if (!chunk || chunk.length - fill < bytes.length) {
|
|
32
|
+
const start = chunk ? starts.at(-1) + chunk.length : 0;
|
|
33
|
+
chunk = new Uint8Array(Math.max(ADD_CHUNK, bytes.length));
|
|
34
|
+
chunks.push(chunk);
|
|
35
|
+
starts.push(start);
|
|
36
|
+
fill = 0;
|
|
37
|
+
}
|
|
38
|
+
chunk.set(bytes, fill);
|
|
39
|
+
const at = starts.at(-1) + fill;
|
|
40
|
+
fill += bytes.length;
|
|
41
|
+
end = at + bytes.length;
|
|
42
|
+
return at;
|
|
43
|
+
},
|
|
44
|
+
/** A view when the range sits in one chunk, else a copy. */
|
|
45
|
+
read(start, length) {
|
|
46
|
+
const i = chunkAt(start);
|
|
47
|
+
const chunk = chunks[i];
|
|
48
|
+
const at = start - starts[i];
|
|
49
|
+
if (at + length <= chunk.length)
|
|
50
|
+
return chunk.subarray(at, at + length);
|
|
51
|
+
const out = new Uint8Array(length);
|
|
52
|
+
let done = 0;
|
|
53
|
+
for (let c = i; done < length; c++) {
|
|
54
|
+
const from = c === i ? at : 0;
|
|
55
|
+
const part = chunks[c].subarray(from, from + length - done);
|
|
56
|
+
out.set(part, done);
|
|
57
|
+
done += part.length;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
},
|
|
61
|
+
get end() {
|
|
62
|
+
return end;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function prefixEnds(pieces) {
|
|
67
|
+
const ends = new Array(pieces.length);
|
|
68
|
+
let at = 0;
|
|
69
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
70
|
+
at += pieces[i].length;
|
|
71
|
+
ends[i] = at;
|
|
72
|
+
}
|
|
73
|
+
return ends;
|
|
74
|
+
}
|
|
75
|
+
/** The index of the piece holding byte `offset`; `pieces.length` at the end. */
|
|
76
|
+
function pieceAt(ends, offset) {
|
|
77
|
+
let lo = 0;
|
|
78
|
+
let hi = ends.length;
|
|
79
|
+
while (lo < hi) {
|
|
80
|
+
const mid = (lo + hi) >> 1;
|
|
81
|
+
if (ends[mid] > offset)
|
|
82
|
+
hi = mid;
|
|
83
|
+
else
|
|
84
|
+
lo = mid + 1;
|
|
85
|
+
}
|
|
86
|
+
return lo;
|
|
87
|
+
}
|
|
88
|
+
function applySplice(pieces, index, remove, insert) {
|
|
89
|
+
return [
|
|
90
|
+
...pieces.slice(0, index),
|
|
91
|
+
...insert,
|
|
92
|
+
...pieces.slice(index + remove),
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
/** The record replacing [offset, offset + length) with `piece`, or nothing. */
|
|
96
|
+
function spliceRecord(pieces, ends, offset, length, piece) {
|
|
97
|
+
let index = pieceAt(ends, offset);
|
|
98
|
+
const pieceStart = (i) => (i === 0 ? 0 : ends[i - 1]);
|
|
99
|
+
const end = offset + length;
|
|
100
|
+
const startsInside = index < pieces.length && pieceStart(index) < offset;
|
|
101
|
+
const last = length > 0 ? pieceAt(ends, end - 1) + 1 : startsInside ? index + 1 : index;
|
|
102
|
+
const removed = pieces.slice(index, last);
|
|
103
|
+
const inserted = [];
|
|
104
|
+
if (startsInside) {
|
|
105
|
+
const first = pieces[index];
|
|
106
|
+
inserted.push({ ...first, length: offset - pieceStart(index) });
|
|
107
|
+
}
|
|
108
|
+
if (piece) {
|
|
109
|
+
const prev = inserted.at(-1) ?? (index > 0 ? pieces[index - 1] : undefined);
|
|
110
|
+
if (prev &&
|
|
111
|
+
prev.buffer === "add" &&
|
|
112
|
+
piece.buffer === "add" &&
|
|
113
|
+
prev.start + prev.length === piece.start) {
|
|
114
|
+
const merged = { ...prev, length: prev.length + piece.length };
|
|
115
|
+
if (inserted.length)
|
|
116
|
+
inserted[inserted.length - 1] = merged;
|
|
117
|
+
else {
|
|
118
|
+
// The piece before the edit grows, so it moves into the record.
|
|
119
|
+
index--;
|
|
120
|
+
removed.unshift(prev);
|
|
121
|
+
inserted.push(merged);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
inserted.push(piece);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (last > 0 && last - 1 >= index && removed.length) {
|
|
129
|
+
const tail = pieces[last - 1];
|
|
130
|
+
const tailEnd = ends[last - 1];
|
|
131
|
+
if (tailEnd > end) {
|
|
132
|
+
inserted.push({
|
|
133
|
+
...tail,
|
|
134
|
+
start: tail.start + tail.length - (tailEnd - end),
|
|
135
|
+
length: tailEnd - end,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { index, removed, inserted };
|
|
140
|
+
}
|
|
141
|
+
export function createHexDocument(source, { resizable = true } = {}) {
|
|
142
|
+
const add = createAddBuffer();
|
|
143
|
+
let pieces = source.size > 0
|
|
144
|
+
? [{ buffer: "source", start: 0, length: source.size }]
|
|
145
|
+
: [];
|
|
146
|
+
let ends = prefixEnds(pieces);
|
|
147
|
+
const undoStack = [];
|
|
148
|
+
const redoStack = [];
|
|
149
|
+
let nextId = 1;
|
|
150
|
+
let savedId = 0;
|
|
151
|
+
let group;
|
|
152
|
+
let changedInGroup = false;
|
|
153
|
+
// Source reads go through a cache keyed by source offsets, which no edit
|
|
154
|
+
// moves, so it stays valid across edits. It reads a sync source in place,
|
|
155
|
+
// and learns which kind it has from the first real read: probing at
|
|
156
|
+
// creation would be a request of its own, and a zero-length one at that.
|
|
157
|
+
const cache = createPageCache(source);
|
|
158
|
+
const listeners = new Set();
|
|
159
|
+
let unsubscribeSource;
|
|
160
|
+
const state = {
|
|
161
|
+
edit: (edit) => editDocument(edit),
|
|
162
|
+
group(fn) {
|
|
163
|
+
if (group) {
|
|
164
|
+
fn();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
group = { step: undefined };
|
|
168
|
+
changedInGroup = false;
|
|
169
|
+
try {
|
|
170
|
+
fn();
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
group = undefined;
|
|
174
|
+
if (changedInGroup)
|
|
175
|
+
notify();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
breakRun() {
|
|
179
|
+
const top = undoStack.at(-1);
|
|
180
|
+
if (top)
|
|
181
|
+
top.key = undefined;
|
|
182
|
+
},
|
|
183
|
+
lastChange: { kind: "edit" },
|
|
184
|
+
version: 0,
|
|
185
|
+
};
|
|
186
|
+
const notify = () => {
|
|
187
|
+
state.version++;
|
|
188
|
+
if (group) {
|
|
189
|
+
changedInGroup = true;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
for (const listener of [...listeners])
|
|
193
|
+
listener();
|
|
194
|
+
};
|
|
195
|
+
const size = () => ends.at(-1) ?? 0;
|
|
196
|
+
/** The pieces' parts covering [offset, end), in order. */
|
|
197
|
+
const segments = (offset, end) => {
|
|
198
|
+
const out = [];
|
|
199
|
+
let i = pieceAt(ends, offset);
|
|
200
|
+
let at = offset;
|
|
201
|
+
while (at < end && i < pieces.length) {
|
|
202
|
+
const piece = pieces[i];
|
|
203
|
+
const pieceStart = ends[i] - piece.length;
|
|
204
|
+
const from = at - pieceStart;
|
|
205
|
+
const length = Math.min(piece.length - from, end - at);
|
|
206
|
+
out.push({ piece, from: piece.start + from, length });
|
|
207
|
+
at += length;
|
|
208
|
+
i++;
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
};
|
|
212
|
+
const join = (parts, length) => {
|
|
213
|
+
if (parts.length === 1)
|
|
214
|
+
return parts[0];
|
|
215
|
+
const out = new Uint8Array(length);
|
|
216
|
+
let at = 0;
|
|
217
|
+
for (const part of parts) {
|
|
218
|
+
out.set(part, at);
|
|
219
|
+
at += part.length;
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
};
|
|
223
|
+
const clamp = (offset, length) => {
|
|
224
|
+
const start = Math.min(Math.max(0, offset), size());
|
|
225
|
+
return [start, Math.min(size(), start + Math.max(0, length))];
|
|
226
|
+
};
|
|
227
|
+
function read(offset, length, signal) {
|
|
228
|
+
const [start, end] = clamp(offset, length);
|
|
229
|
+
const parts = segments(start, end);
|
|
230
|
+
if (cache.sync() === false && signal?.aborted) {
|
|
231
|
+
return Promise.reject(abortError(signal));
|
|
232
|
+
}
|
|
233
|
+
const read = parts.map(({ piece, from, length }) => piece.buffer === "add"
|
|
234
|
+
? add.read(from, length)
|
|
235
|
+
: cache.read(from, length, signal));
|
|
236
|
+
// A document answers in place only once its source has: a view that saw
|
|
237
|
+
// edited bytes come back at once would take an async document for a
|
|
238
|
+
// sync one. With no source byte in this read to learn from, the source's
|
|
239
|
+
// first page decides; it is cached, and undo may well need it. An empty
|
|
240
|
+
// source has no bytes to read, so it never decides.
|
|
241
|
+
if (cache.sync() === undefined &&
|
|
242
|
+
source.size > 0 &&
|
|
243
|
+
parts.length > 0 &&
|
|
244
|
+
parts.every(({ piece }) => piece.buffer === "add")) {
|
|
245
|
+
const first = cache.read(0, 1);
|
|
246
|
+
if (first instanceof Promise)
|
|
247
|
+
first.catch(() => { });
|
|
248
|
+
}
|
|
249
|
+
const sync = cache.sync() ?? source.size === 0;
|
|
250
|
+
if (sync && read.every((r) => r instanceof Uint8Array)) {
|
|
251
|
+
return join(read, end - start);
|
|
252
|
+
}
|
|
253
|
+
return Promise.all(read).then((bytes) => join(bytes, end - start));
|
|
254
|
+
}
|
|
255
|
+
const readInPlace = (offset, length) => {
|
|
256
|
+
const bytes = source.read(offset, length);
|
|
257
|
+
if (bytes instanceof Uint8Array)
|
|
258
|
+
return bytes;
|
|
259
|
+
bytes.catch(() => { });
|
|
260
|
+
return undefined;
|
|
261
|
+
};
|
|
262
|
+
function peek(offset, length) {
|
|
263
|
+
const [start, end] = clamp(offset, length);
|
|
264
|
+
const parts = [];
|
|
265
|
+
for (const { piece, from, length } of segments(start, end)) {
|
|
266
|
+
let bytes;
|
|
267
|
+
if (piece.buffer === "add")
|
|
268
|
+
bytes = add.read(from, length);
|
|
269
|
+
else if (cache.sync() === false)
|
|
270
|
+
bytes = cache.get(from, length);
|
|
271
|
+
else if (cache.sync())
|
|
272
|
+
bytes = readInPlace(from, length);
|
|
273
|
+
// Unknown until the first read, and finding out would be one.
|
|
274
|
+
else
|
|
275
|
+
bytes = source.peek?.(from, length);
|
|
276
|
+
if (!bytes)
|
|
277
|
+
return undefined;
|
|
278
|
+
parts.push(bytes);
|
|
279
|
+
}
|
|
280
|
+
return parts.length ? join(parts, end - start) : new Uint8Array(0);
|
|
281
|
+
}
|
|
282
|
+
const apply = (record, forward) => {
|
|
283
|
+
const [remove, insert] = forward
|
|
284
|
+
? [record.removed, record.inserted]
|
|
285
|
+
: [record.inserted, record.removed];
|
|
286
|
+
pieces = applySplice(pieces, record.index, remove.length, insert);
|
|
287
|
+
ends = prefixEnds(pieces);
|
|
288
|
+
};
|
|
289
|
+
function editDocument({ offset, length, bytes, key, before, after, }) {
|
|
290
|
+
const [start, end] = clamp(offset, length);
|
|
291
|
+
if (end === start && bytes.length === 0)
|
|
292
|
+
return;
|
|
293
|
+
if (!resizable && bytes.length !== end - start) {
|
|
294
|
+
throw new RangeError("This document's size is locked");
|
|
295
|
+
}
|
|
296
|
+
const piece = bytes.length
|
|
297
|
+
? { buffer: "add", start: add.append(bytes), length: bytes.length }
|
|
298
|
+
: undefined;
|
|
299
|
+
const record = spliceRecord(pieces, ends, start, end - start, piece);
|
|
300
|
+
apply(record, true);
|
|
301
|
+
const top = undoStack.at(-1);
|
|
302
|
+
const joinsGroup = group?.step !== undefined && group.step === top;
|
|
303
|
+
const continuesRun = key !== undefined &&
|
|
304
|
+
top?.key === key &&
|
|
305
|
+
redoStack.length === 0 &&
|
|
306
|
+
(start === top.end || (end > start && end === top.end));
|
|
307
|
+
if (top && (joinsGroup || continuesRun)) {
|
|
308
|
+
top.records.push(record);
|
|
309
|
+
top.end = start + bytes.length;
|
|
310
|
+
if (after !== undefined)
|
|
311
|
+
top.after = after;
|
|
312
|
+
top.key = key;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
const step = {
|
|
316
|
+
id: nextId++,
|
|
317
|
+
records: [record],
|
|
318
|
+
before,
|
|
319
|
+
after,
|
|
320
|
+
key,
|
|
321
|
+
end: start + bytes.length,
|
|
322
|
+
};
|
|
323
|
+
undoStack.push(step);
|
|
324
|
+
redoStack.length = 0;
|
|
325
|
+
if (group)
|
|
326
|
+
group.step = step;
|
|
327
|
+
}
|
|
328
|
+
state.lastChange = { kind: "edit" };
|
|
329
|
+
notify();
|
|
330
|
+
}
|
|
331
|
+
const currentId = () => undoStack.at(-1)?.id ?? 0;
|
|
332
|
+
const doc = {
|
|
333
|
+
source,
|
|
334
|
+
resizable,
|
|
335
|
+
get size() {
|
|
336
|
+
return size();
|
|
337
|
+
},
|
|
338
|
+
get dirty() {
|
|
339
|
+
return currentId() !== savedId;
|
|
340
|
+
},
|
|
341
|
+
get canUndo() {
|
|
342
|
+
return undoStack.length > 0;
|
|
343
|
+
},
|
|
344
|
+
get canRedo() {
|
|
345
|
+
return redoStack.length > 0;
|
|
346
|
+
},
|
|
347
|
+
read,
|
|
348
|
+
peek,
|
|
349
|
+
subscribe(listener) {
|
|
350
|
+
listeners.add(listener);
|
|
351
|
+
if (listeners.size === 1) {
|
|
352
|
+
unsubscribeSource = source.subscribe?.(() => {
|
|
353
|
+
cache.invalidate();
|
|
354
|
+
state.lastChange = { kind: "other" };
|
|
355
|
+
notify();
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return () => {
|
|
359
|
+
if (!listeners.delete(listener) || listeners.size > 0)
|
|
360
|
+
return;
|
|
361
|
+
unsubscribeSource?.();
|
|
362
|
+
unsubscribeSource = undefined;
|
|
363
|
+
};
|
|
364
|
+
},
|
|
365
|
+
replace(offset, length, bytes) {
|
|
366
|
+
editDocument({ offset, length, bytes });
|
|
367
|
+
},
|
|
368
|
+
insert(offset, bytes) {
|
|
369
|
+
editDocument({ offset, length: 0, bytes });
|
|
370
|
+
},
|
|
371
|
+
overwrite(offset, bytes) {
|
|
372
|
+
const [start] = clamp(offset, 0);
|
|
373
|
+
const room = size() - start;
|
|
374
|
+
const written = resizable ? bytes : bytes.subarray(0, room);
|
|
375
|
+
editDocument({
|
|
376
|
+
offset: start,
|
|
377
|
+
length: Math.min(written.length, room),
|
|
378
|
+
bytes: written,
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
delete(offset, length) {
|
|
382
|
+
editDocument({ offset, length, bytes: new Uint8Array(0) });
|
|
383
|
+
},
|
|
384
|
+
undo() {
|
|
385
|
+
const step = undoStack.pop();
|
|
386
|
+
if (!step)
|
|
387
|
+
return;
|
|
388
|
+
for (let i = step.records.length - 1; i >= 0; i--) {
|
|
389
|
+
apply(step.records[i], false);
|
|
390
|
+
}
|
|
391
|
+
step.key = undefined;
|
|
392
|
+
redoStack.push(step);
|
|
393
|
+
state.lastChange = { kind: "undo", selection: step.before };
|
|
394
|
+
notify();
|
|
395
|
+
},
|
|
396
|
+
redo() {
|
|
397
|
+
const step = redoStack.pop();
|
|
398
|
+
if (!step)
|
|
399
|
+
return;
|
|
400
|
+
for (const record of step.records)
|
|
401
|
+
apply(record, true);
|
|
402
|
+
undoStack.push(step);
|
|
403
|
+
state.lastChange = { kind: "redo", selection: step.after };
|
|
404
|
+
notify();
|
|
405
|
+
},
|
|
406
|
+
markSaved() {
|
|
407
|
+
state.breakRun();
|
|
408
|
+
if (savedId === currentId())
|
|
409
|
+
return;
|
|
410
|
+
savedId = currentId();
|
|
411
|
+
state.lastChange = { kind: "other" };
|
|
412
|
+
notify();
|
|
413
|
+
},
|
|
414
|
+
modifiedRanges(offset, length) {
|
|
415
|
+
const [start, end] = clamp(offset, length);
|
|
416
|
+
const out = [];
|
|
417
|
+
let at = start;
|
|
418
|
+
for (const { piece, length } of segments(start, end)) {
|
|
419
|
+
if (piece.buffer === "add") {
|
|
420
|
+
const last = out.at(-1);
|
|
421
|
+
if (last && last[1] === at)
|
|
422
|
+
last[1] = at + length;
|
|
423
|
+
else
|
|
424
|
+
out.push([at, at + length]);
|
|
425
|
+
}
|
|
426
|
+
at += length;
|
|
427
|
+
}
|
|
428
|
+
return out;
|
|
429
|
+
},
|
|
430
|
+
pieces() {
|
|
431
|
+
return pieces.map((piece) => piece.buffer === "add"
|
|
432
|
+
? { kind: "bytes", bytes: add.read(piece.start, piece.length) }
|
|
433
|
+
: { kind: "source", offset: piece.start, length: piece.length });
|
|
434
|
+
},
|
|
435
|
+
async toBlob(type) {
|
|
436
|
+
const snapshot = doc.pieces();
|
|
437
|
+
const wrapped = source;
|
|
438
|
+
const parts = [];
|
|
439
|
+
for (const piece of snapshot) {
|
|
440
|
+
if (piece.kind === "bytes") {
|
|
441
|
+
parts.push(piece.bytes);
|
|
442
|
+
}
|
|
443
|
+
else if (wrapped.blob instanceof Blob) {
|
|
444
|
+
parts.push(wrapped.blob.slice(piece.offset, piece.offset + piece.length));
|
|
445
|
+
}
|
|
446
|
+
else if (wrapped.bytes instanceof Uint8Array) {
|
|
447
|
+
parts.push(wrapped.bytes.subarray(piece.offset, piece.offset + piece.length));
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
for (let at = 0; at < piece.length; at += EXPORT_CHUNK) {
|
|
451
|
+
const bytes = await source.read(piece.offset + at, Math.min(EXPORT_CHUNK, piece.length - at));
|
|
452
|
+
// A sync source may hand out a view it later changes.
|
|
453
|
+
parts.push(bytes.slice());
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return new Blob(parts, { type });
|
|
458
|
+
},
|
|
459
|
+
stream() {
|
|
460
|
+
const snapshot = doc.pieces();
|
|
461
|
+
const controller = new AbortController();
|
|
462
|
+
let index = 0;
|
|
463
|
+
let at = 0;
|
|
464
|
+
return new ReadableStream({
|
|
465
|
+
async pull(stream) {
|
|
466
|
+
const piece = snapshot[index];
|
|
467
|
+
if (!piece) {
|
|
468
|
+
stream.close();
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (piece.kind === "bytes") {
|
|
472
|
+
index++;
|
|
473
|
+
stream.enqueue(piece.bytes.slice());
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const length = Math.min(EXPORT_CHUNK, piece.length - at);
|
|
477
|
+
const bytes = await source.read(piece.offset + at, length, controller.signal);
|
|
478
|
+
at += length;
|
|
479
|
+
if (at >= piece.length) {
|
|
480
|
+
index++;
|
|
481
|
+
at = 0;
|
|
482
|
+
}
|
|
483
|
+
stream.enqueue(bytes.slice());
|
|
484
|
+
},
|
|
485
|
+
cancel(reason) {
|
|
486
|
+
controller.abort(reason);
|
|
487
|
+
},
|
|
488
|
+
}, { highWaterMark: 1 });
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
internals.set(doc, state);
|
|
492
|
+
return doc;
|
|
493
|
+
}
|
|
494
|
+
function internalsOf(doc) {
|
|
495
|
+
const state = internals.get(doc);
|
|
496
|
+
if (!state)
|
|
497
|
+
throw new TypeError("Not a HexDocument from createHexDocument");
|
|
498
|
+
return state;
|
|
499
|
+
}
|
|
500
|
+
export function isHexDocument(value) {
|
|
501
|
+
return typeof value === "object" && value !== null && internals.has(value);
|
|
502
|
+
}
|
|
503
|
+
/** An edit carrying the editor's selections and a coalescing key; `replace` without them. */
|
|
504
|
+
export function editDocument(doc, edit) {
|
|
505
|
+
internalsOf(doc).edit(edit);
|
|
506
|
+
}
|
|
507
|
+
/** Runs several edits as one undo step, with one notification. */
|
|
508
|
+
export function editGroup(doc, fn) {
|
|
509
|
+
internalsOf(doc).group(fn);
|
|
510
|
+
}
|
|
511
|
+
/** The next edit starts a new undo step even when it would continue the run. */
|
|
512
|
+
export function breakRun(doc) {
|
|
513
|
+
internalsOf(doc).breakRun();
|
|
514
|
+
}
|
|
515
|
+
/** What the most recent notification was for; undo and redo carry the selection to restore. */
|
|
516
|
+
export function lastChange(doc) {
|
|
517
|
+
return internalsOf(doc).lastChange;
|
|
518
|
+
}
|
|
519
|
+
/** Bumped by every change the document notifies. */
|
|
520
|
+
export function documentVersion(doc) {
|
|
521
|
+
return internalsOf(doc).version;
|
|
522
|
+
}
|
|
523
|
+
//# sourceMappingURL=document.js.map
|