vuehex 0.5.7 → 0.6.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # VueHex
2
2
 
3
- VueHex is a fast, virtualized hex viewer component for Vue 3. It can be used both for cleanly displaying binary data and for efficiently viewing very large datasets.
3
+ VueHex is a fast, virtualized hex viewer/editor component for Vue 3. It can be used both for cleanly viewing and editing binary data and for efficiently handling very large datasets.
4
4
 
5
5
  * No dependencies
6
6
  * Small package (~64KB minimized / ~15KB zipped)
@@ -68,6 +68,7 @@ const windowData = ref(backingFile.slice(0, 16 * 48));
68
68
  | `statusbar` | `null` | Status bar placement: `'top'`, `'bottom'`, or `null` to hide. Shows byte info on hover and selection details. |
69
69
  | `statusbarLayout` | — | Configuration object controlling which items appear in the status bar and their placement (`left`, `middle`, `right` sections). |
70
70
  | `cursor` | `false` | Enable keyboard/click cursor navigation. Navigate with arrow keys when focused or click bytes to move cursor. |
71
+ | `editable` | `false` | Enable editor mode. Typing/paste/cut emit `edit` intents. Cursor is automatically enabled while editing. |
71
72
 
72
73
  ## Models
73
74
 
@@ -86,12 +87,101 @@ VueHex emits several events to enable interactive features:
86
87
  | Event | Payload | Description |
87
88
  |-------|---------|-------------|
88
89
  | `updateVirtualData` | `{ offset: number, length: number }` | Emitted when the component needs more data in windowed mode. Load the requested byte range and update `v-model` with the new data. |
90
+ | `edit` | `VueHexEditIntent` | Emitted when `editable` is enabled and the user types, deletes, pastes, or cuts. In windowed mode, the parent should apply the intent to the backing store and refresh `v-model`. |
89
91
  | `byte-click` | `{ index: number, byte: number, kind: 'hex' \| 'ascii' }` | Emitted when a user clicks on a specific byte cell. `index` is the absolute byte position, `byte` is the value (0-255), and `kind` indicates whether the hex or ASCII column was clicked. |
90
92
  | `selection-change` | `{ start: number \| null, end: number \| null, length: number }` | Emitted when the selection range changes. `start` and `end` are absolute byte positions (inclusive), or `null` if nothing is selected. `length` is the number of selected bytes. |
91
93
  | `row-hover-on` / `row-hover-off` | `{ offset: number }` | Emitted when hovering over/leaving a row. |
92
94
  | `hex-hover-on` / `hex-hover-off` | `{ index: number, byte: number }` | Emitted when hovering over/leaving a hex cell. |
93
95
  | `ascii-hover-on` / `ascii-hover-off` | `{ index: number, byte: number }` | Emitted when hovering over/leaving an ASCII cell. |
94
96
 
97
+ ## Editable mode
98
+
99
+ Set `editable` to turn VueHex into a hex editor. VueHex emits a single `edit` event describing user intent.
100
+
101
+ Editing highlights:
102
+
103
+ - Click hex vs ASCII to choose the active column (or press `Tab` to toggle).
104
+ - Press `Insert` to toggle insert/overwrite mode.
105
+ - Hex typing commits after two nibbles.
106
+ - Selection integrates with editing: typing/paste replaces the selection; `Delete`/`Backspace` delete the selection.
107
+ - Clipboard shortcuts:
108
+ - `Ctrl/Cmd+C` copy selection
109
+ - `Ctrl/Cmd+V` paste (hex column treats clipboard as hex bytes; whitespace is ignored)
110
+ - `Ctrl/Cmd+X` cut (copy + delete selection)
111
+ - Undo/redo shortcuts:
112
+ - `Ctrl/Cmd+Z` undo
113
+ - `Ctrl/Cmd+Y` redo (also `Ctrl/Cmd+Shift+Z`)
114
+
115
+ ### Buffer mode (self-managed)
116
+
117
+ In `data-mode="buffer"`, VueHex will apply edits to `v-model` automatically. Listening to `@edit` is optional.
118
+
119
+ Undo/redo is built-in in this mode and is implemented as a compact history of edit diffs (not full buffer snapshots).
120
+
121
+ ```vue
122
+ <template>
123
+ <VueHex v-model="bytes" data-mode="buffer" editable style="height: 320px" />
124
+ </template>
125
+
126
+ <script setup lang="ts">
127
+ import { ref } from "vue";
128
+ import VueHex from "vuehex";
129
+
130
+ const bytes = ref(new Uint8Array(await file.arrayBuffer()));
131
+ </script>
132
+ ```
133
+
134
+ ### Window mode (parent-managed)
135
+
136
+ In `data-mode="window"`, VueHex does not own the full dataset. The parent is responsible for applying `edit` intents to the backing store and then refreshing `windowData`.
137
+
138
+ For undo/redo in windowed mode, VueHex emits `{ kind: "undo" }` / `{ kind: "redo" }` intents on the `edit` event and the parent should implement history.
139
+
140
+ ```vue
141
+ <template>
142
+ <VueHex
143
+ v-model="windowData"
144
+ data-mode="window"
145
+ :window-offset="windowOffset"
146
+ :total-size="store.length"
147
+ :get-selection-data="getSelectionData"
148
+ editable
149
+ style="height: 320px"
150
+ @updateVirtualData="handleUpdateVirtualData"
151
+ @edit="handleEdit"
152
+ />
153
+ </template>
154
+
155
+ <script setup lang="ts">
156
+ import { ref } from "vue";
157
+ import VueHex, { type VueHexEditIntent, type VueHexWindowRequest } from "vuehex";
158
+
159
+ const store = ref(new Uint8Array(await file.arrayBuffer()));
160
+ const windowOffset = ref(0);
161
+ const windowData = ref(new Uint8Array());
162
+ let windowLength = 0x4000;
163
+
164
+ function handleUpdateVirtualData(request: VueHexWindowRequest) {
165
+ windowOffset.value = request.offset;
166
+ windowLength = request.length ?? windowLength;
167
+ windowData.value = store.value.slice(windowOffset.value, windowOffset.value + windowLength);
168
+ }
169
+
170
+ function getSelectionData(start: number, end: number) {
171
+ const from = Math.min(start, end);
172
+ const to = Math.max(start, end);
173
+ return store.value.slice(from, to + 1);
174
+ }
175
+
176
+ function handleEdit(intent: VueHexEditIntent) {
177
+ // Apply the intent to your backing store, then refresh windowData.
178
+ // (See Storybook "Editable (windowed)" / docs guide for a complete apply function.)
179
+ store.value = applyEditIntent(store.value, intent);
180
+ windowData.value = store.value.slice(windowOffset.value, windowOffset.value + windowLength);
181
+ }
182
+ </script>
183
+ ```
184
+
95
185
  ## Styling options
96
186
 
97
187
  1. Import `vuehex/styles` for the default look.
@@ -117,7 +207,7 @@ VueHex solves this with optimized **virtual scrolling**:
117
207
 
118
208
  The above technique works very well, but unfortunately it has a limit. Browsers impose limits on element dimensions to prevent rendering engine crashes. Most modern browsers cap `max-height` at around **33,554,432 pixels** (Chrome/Edge) or **17,895,698 pixels** (Firefox). Once your calculated scroll container height exceeds this limit, virtualization breaks—the scrollbar becomes inaccurate, and you can't reach data beyond the cap.
119
209
 
120
- For a hex viewer with 16 bytes per row and 24px row height:
210
+ For a hex viewer/editor with 16 bytes per row and 24px row height:
121
211
  - **Firefox limit:** ~746,000 rows = ~11.4 MB of data
122
212
  - **Chrome limit:** ~1.4 million rows = ~21.4 MB of data
123
213
 
@@ -358,17 +448,72 @@ Customize individual chunk items with access to chunk data and active state:
358
448
 
359
449
  Both slots are optional. If not provided, VueHex uses the default chunk navigator appearance.
360
450
 
361
- ## Status Bar Slots
451
+ ## Status bar
452
+
453
+ Enable the status bar by setting `statusbar="top"` or `statusbar="bottom"`. You can control which items appear (and where) using `statusbarLayout`.
454
+
455
+ ### Layout
456
+
457
+ `statusbarLayout` splits the status bar into three sections:
458
+
459
+ - `left` (left-aligned)
460
+ - `middle` (centered)
461
+ - `right` (right-aligned)
362
462
 
363
- When using the status bar (`statusbar="top"` or `statusbar="bottom"`), you can provide custom content via slots. Use the `statusbarLayout` prop with the `"slot"` component name to control where your custom content appears.
463
+ Each section is an array of **status bar components** (strings or `{ name, config }` objects). Unknown component names are ignored.
364
464
 
365
- ### Available slots
465
+ When `statusbarLayout` is omitted, VueHex defaults to:
366
466
 
367
- - `#statusbar-left` - Content for the left section
368
- - `#statusbar-middle` - Content for the middle section
369
- - `#statusbar-right` - Content for the right section
467
+ - `left: ["offset", "hex", "ascii"]`
468
+ - `right: ["selection"]`
370
469
 
371
- ### Example
470
+ ### Built-in components
471
+
472
+ - `offset`: hovered byte offset (respects `uppercase` + `isPrintable`/`renderAscii` where relevant)
473
+ - `hex`: hovered byte value as a 2-digit hex string
474
+ - `ascii`: hovered byte rendered as ASCII (or `nonPrintableChar`)
475
+ - `selection`: selection summary (`"<count> bytes (start–end)"`)
476
+ - `editable`: editor state label (`VIEW` / `EDIT`)
477
+ - `mode`: editor mode (`INS` / `OVR`) or placeholder when not available
478
+ - `column`: active editor column (`HEX` / `ASCII`) or placeholder when not available
479
+ - `total`: total size in bytes (derived from `totalSize` when provided; otherwise `v-model.length`). Does not include the EOF "ghost" cell.
480
+ - `slot`: renders one of the status bar slots (see below)
481
+
482
+ Notes:
483
+
484
+ - The hover-driven items (`offset`, `hex`, `ascii`) reflect the cell under the mouse, not the keyboard cursor.
485
+ - Editor items (`editable`, `mode`, `column`) render whenever they are included in the layout (they do not auto-hide when `editable` is false).
486
+
487
+ ### Per-component configuration (`config`)
488
+
489
+ All built-ins support:
490
+
491
+ - `label` (string): overrides the label text
492
+ - `valueMinWidth` (string): CSS length (e.g. `"10ch"`) for stable widths
493
+ - `valueWidth` (string): CSS length (e.g. `"120px"`)
494
+
495
+ Component-specific keys:
496
+
497
+ - `offset`: `format` (`"hex"` or `"decimal"`), `pad` (number), `prefix` (boolean)
498
+ - `hex`: `prefix` (boolean)
499
+ - `ascii`: `quote` (boolean)
500
+ - `selection`: `showWhenEmpty` (boolean)
501
+ - `editable`: `short` (boolean)
502
+ - `mode`: `short` (boolean), `placeholder` (string)
503
+ - `column`: `short` (boolean), `placeholder` (string)
504
+ - `total`: `format` (`"human"` or `"hex"`), `decimals` (number, for `"human"`), `unit` (boolean), `pad` (number, for `"hex"`), `prefix` (boolean, for `"hex"`)
505
+
506
+ ### Slot components
507
+
508
+ Use the built-in `"slot"` component name to render your own custom content inside the status bar.
509
+
510
+ Available slots:
511
+
512
+ - `#statusbar-left`
513
+ - `#statusbar-middle`
514
+ - `#statusbar-right`
515
+
516
+ Example:
372
517
 
373
518
  ```vue
374
519
  <VueHex
@@ -377,20 +522,20 @@ When using the status bar (`statusbar="top"` or `statusbar="bottom"`), you can p
377
522
  :statusbar-layout="{
378
523
  left: ['offset', 'slot', 'hex'],
379
524
  middle: ['ascii'],
380
- right: ['selection', 'slot']
525
+ right: ['selection', 'slot'],
381
526
  }"
382
527
  >
383
528
  <template #statusbar-left>
384
529
  <span>Mode: RO</span>
385
530
  </template>
386
-
531
+
387
532
  <template #statusbar-right>
388
533
  <span>Endian: LE</span>
389
534
  </template>
390
535
  </VueHex>
391
536
  ```
392
537
 
393
- The `"slot"` entry in `statusbarLayout` determines where your custom content renders relative to built-in status bar items. You can place it at any position within each section array.
538
+ The `"slot"` entry controls where your slot content renders relative to built-in items.
394
539
 
395
540
  ## License
396
541