vuehex 0.6.3 → 0.6.5
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 +5 -3
- package/dist/components/VueHex.vue.d.ts +217 -0
- package/dist/components/VueHexChunkNavigator.vue.d.ts +49 -0
- package/dist/components/VueHexStatusBar.vue.d.ts +55 -0
- package/dist/components/composables/useChunking.d.ts +66 -0
- package/dist/components/composables/useCursor.d.ts +57 -0
- package/dist/components/composables/useDataMode.d.ts +36 -0
- package/dist/components/composables/useEditing.d.ts +28 -0
- package/dist/components/composables/useHexWindow.d.ts +104 -0
- package/dist/components/composables/useHoverLinking.d.ts +39 -0
- package/dist/components/composables/useSelection.d.ts +91 -0
- package/dist/components/composables/useVueHexTheme.d.ts +35 -0
- package/dist/components/vuehex-api.d.ts +162 -0
- package/dist/components/vuehex-resolvers.d.ts +11 -0
- package/dist/components/vuehex-utils.d.ts +41 -0
- package/dist/index.d.ts +13 -0
- package/package.json +5 -8
- package/dist/styles.js +0 -0
- /package/dist/{styles.css → index.css} +0 -0
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Register the plugin once to make the `<VueHex>` component available everywhere a
|
|
|
29
29
|
```ts
|
|
30
30
|
import { createApp } from "vue";
|
|
31
31
|
import VueHex from "vuehex";
|
|
32
|
-
import "vuehex/
|
|
32
|
+
import "vuehex/style.css";
|
|
33
33
|
|
|
34
34
|
import App from "./App.vue";
|
|
35
35
|
|
|
@@ -38,6 +38,8 @@ app.use(VueHex);
|
|
|
38
38
|
app.mount("#app");
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
`vuehex/style.css` resolves to the package's bundled stylesheet.
|
|
42
|
+
|
|
41
43
|
## Quick start
|
|
42
44
|
|
|
43
45
|
It can be as simple as this
|
|
@@ -192,7 +194,7 @@ function handleEdit(intent: VueHexEditIntent) {
|
|
|
192
194
|
|
|
193
195
|
## Styling options
|
|
194
196
|
|
|
195
|
-
1. Import `vuehex/
|
|
197
|
+
1. Import `vuehex/style.css` for the default look.
|
|
196
198
|
2. Pass `theme="dark" | "light" | "terminal" | "sunset" | "auto"` to toggle bundled palettes explicitly (or skip the prop entirely to stick with OS detection).
|
|
197
199
|
3. Roll your own styles targeting the emitted class names (`.vuehex`, `.vuehex-byte`, `.vuehex-ascii-char`, etc.). The default sheet sets `.vuehex { height: 100%; }`, so remember to give the wrapper a concrete height.
|
|
198
200
|
|
|
@@ -606,4 +608,4 @@ Bug reports are most actionable with:
|
|
|
606
608
|
|
|
607
609
|
## License
|
|
608
610
|
|
|
609
|
-
MIT © Vincent Vollers
|
|
611
|
+
MIT © Vincent Vollers
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { VueHexAsciiRenderer, VueHexCellClassResolverInput, VueHexEditIntent, VueHexPrintableCheck, VueHexStatusBarLayout, VueHexWindowRequest } from "./vuehex-api";
|
|
2
|
+
/**
|
|
3
|
+
* VueHex - A performant, virtualized hex editor component for Vue 3.
|
|
4
|
+
*
|
|
5
|
+
* Supports three data modes:
|
|
6
|
+
* - `buffer`: Full dataset in `v-model` (no windowing)
|
|
7
|
+
* - `window`: Partial dataset in `v-model`, emits `updateVirtualData` for more
|
|
8
|
+
* - `auto`: Infers mode from `windowOffset` and `totalSize` props
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```vue
|
|
12
|
+
* <VueHex
|
|
13
|
+
* v-model="bytes"
|
|
14
|
+
* :total-size="fileSize"
|
|
15
|
+
* data-mode="window"
|
|
16
|
+
* @update-virtual-data="loadWindow"
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
interface VueHexProps {
|
|
21
|
+
/**
|
|
22
|
+
* Controls whether VueHex treats `v-model` as the full buffer or as a virtual window.
|
|
23
|
+
*
|
|
24
|
+
* - `auto` (default): infer based on `windowOffset` / `totalSize` vs `modelValue.length`.
|
|
25
|
+
* - `buffer`: `v-model` is the entire dataset (no window requests).
|
|
26
|
+
* - `window`: `v-model` is a slice; VueHex may emit `updateVirtualData`.
|
|
27
|
+
*/
|
|
28
|
+
dataMode?: "auto" | "buffer" | "window";
|
|
29
|
+
/**
|
|
30
|
+
* When true, disables internal scrolling/virtualization and expands the component height
|
|
31
|
+
* to fit the entire dataset.
|
|
32
|
+
*
|
|
33
|
+
* In this mode, VueHex expects the full data buffer in `v-model` (windowing is not used).
|
|
34
|
+
*/
|
|
35
|
+
expandToContent?: boolean;
|
|
36
|
+
/** Total bytes available in the backing source; defaults to modelValue length. */
|
|
37
|
+
totalSize?: number;
|
|
38
|
+
/** Controls how many bytes render per table row; consumers tune readability. */
|
|
39
|
+
bytesPerRow?: number;
|
|
40
|
+
/** When true, renders hexadecimal digits in uppercase for stylistic preference. */
|
|
41
|
+
uppercase?: boolean;
|
|
42
|
+
/** Substitute character used for non-printable bytes in the ASCII pane. */
|
|
43
|
+
nonPrintableChar?: string;
|
|
44
|
+
/** Number of rows to pre-render above/below the viewport to reduce flicker. */
|
|
45
|
+
overscan?: number;
|
|
46
|
+
/** Optional custom printable check letting consumers broaden the ASCII range. */
|
|
47
|
+
isPrintable?: VueHexPrintableCheck;
|
|
48
|
+
/** Optional renderer for printable ASCII cells so callers can supply glyphs. */
|
|
49
|
+
renderAscii?: VueHexAsciiRenderer;
|
|
50
|
+
/** Theme token appended to classes, enabling consumer-provided styling. */
|
|
51
|
+
theme?: string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Hook for assigning classes per cell, useful for highlighting bytes of interest.
|
|
54
|
+
*
|
|
55
|
+
* Pass an array to layer multiple resolvers; results are merged in order.
|
|
56
|
+
*
|
|
57
|
+
* When omitted/undefined, VueHex applies the built-in ASCII category highlighting.
|
|
58
|
+
* To disable all highlighting, pass null.
|
|
59
|
+
*/
|
|
60
|
+
cellClassForByte?: VueHexCellClassResolverInput | null;
|
|
61
|
+
/**
|
|
62
|
+
* Optional selection provider used for clipboard copy.
|
|
63
|
+
* Should return the raw bytes between selectionStart and selectionEnd (inclusive).
|
|
64
|
+
*/
|
|
65
|
+
getSelectionData?: import("./vuehex-api").VueHexSelectionDataProvider;
|
|
66
|
+
/** Toggles the chunk navigator UI, allowing quick jumps through large files. */
|
|
67
|
+
showChunkNavigator?: boolean;
|
|
68
|
+
/** Places the chunk navigator around the viewer to fit various layouts. */
|
|
69
|
+
chunkNavigatorPlacement?: "left" | "right" | "top" | "bottom";
|
|
70
|
+
/**
|
|
71
|
+
* Optional status bar placement.
|
|
72
|
+
*
|
|
73
|
+
* When set to "top" or "bottom", the status bar is shown.
|
|
74
|
+
* When omitted/null, the status bar is hidden.
|
|
75
|
+
*/
|
|
76
|
+
statusbar?: "top" | "bottom" | null;
|
|
77
|
+
/** When true, enables keyboard/click cursor navigation and rendering. */
|
|
78
|
+
cursor?: boolean;
|
|
79
|
+
/** When true, enables editor mode (typing emits edit intents). */
|
|
80
|
+
editable?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Configures which items appear in the status bar and where they render.
|
|
83
|
+
*
|
|
84
|
+
* The status bar is split into three sections: left, middle, right.
|
|
85
|
+
*/
|
|
86
|
+
statusbarLayout?: VueHexStatusBarLayout | null;
|
|
87
|
+
}
|
|
88
|
+
type __VLS_Props = VueHexProps;
|
|
89
|
+
type __VLS_ModelProps = {
|
|
90
|
+
"windowOffset"?: number;
|
|
91
|
+
modelValue?: Uint8Array;
|
|
92
|
+
"cursorLocation"?: number | null;
|
|
93
|
+
};
|
|
94
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
95
|
+
declare var __VLS_11: {
|
|
96
|
+
chunks: import("./VueHexChunkNavigator.vue").ChunkDescriptor[];
|
|
97
|
+
activeIndex: number;
|
|
98
|
+
}, __VLS_14: {
|
|
99
|
+
chunk: import("./VueHexChunkNavigator.vue").ChunkDescriptor;
|
|
100
|
+
active: boolean;
|
|
101
|
+
select: () => void;
|
|
102
|
+
}, __VLS_25: {}, __VLS_28: {}, __VLS_31: {};
|
|
103
|
+
type __VLS_Slots = {} & {
|
|
104
|
+
'chunk-navigator-header'?: (props: typeof __VLS_11) => any;
|
|
105
|
+
} & {
|
|
106
|
+
'chunk-navigator-item'?: (props: typeof __VLS_14) => any;
|
|
107
|
+
} & {
|
|
108
|
+
'statusbar-left'?: (props: typeof __VLS_25) => any;
|
|
109
|
+
} & {
|
|
110
|
+
'statusbar-middle'?: (props: typeof __VLS_28) => any;
|
|
111
|
+
} & {
|
|
112
|
+
'statusbar-right'?: (props: typeof __VLS_31) => any;
|
|
113
|
+
};
|
|
114
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
115
|
+
scrollToByte: (offset: number) => void;
|
|
116
|
+
selectChunk: (index: number) => boolean;
|
|
117
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
118
|
+
"update:windowOffset": (value: number) => any;
|
|
119
|
+
"update:modelValue": (value: Uint8Array<ArrayBufferLike>) => any;
|
|
120
|
+
"update:cursorLocation": (value: number | null) => any;
|
|
121
|
+
} & {
|
|
122
|
+
"row-hover-on": (payload: {
|
|
123
|
+
offset: number;
|
|
124
|
+
}) => any;
|
|
125
|
+
"row-hover-off": (payload: {
|
|
126
|
+
offset: number;
|
|
127
|
+
}) => any;
|
|
128
|
+
"hex-hover-on": (payload: {
|
|
129
|
+
index: number;
|
|
130
|
+
byte: number;
|
|
131
|
+
}) => any;
|
|
132
|
+
"hex-hover-off": (payload: {
|
|
133
|
+
index: number;
|
|
134
|
+
byte: number;
|
|
135
|
+
}) => any;
|
|
136
|
+
"ascii-hover-on": (payload: {
|
|
137
|
+
index: number;
|
|
138
|
+
byte: number;
|
|
139
|
+
}) => any;
|
|
140
|
+
"ascii-hover-off": (payload: {
|
|
141
|
+
index: number;
|
|
142
|
+
byte: number;
|
|
143
|
+
}) => any;
|
|
144
|
+
updateVirtualData: (payload: VueHexWindowRequest) => any;
|
|
145
|
+
edit: (payload: VueHexEditIntent) => any;
|
|
146
|
+
"byte-click": (payload: {
|
|
147
|
+
index: number;
|
|
148
|
+
byte: number;
|
|
149
|
+
kind: "hex" | "ascii";
|
|
150
|
+
}) => any;
|
|
151
|
+
"selection-change": (payload: {
|
|
152
|
+
start: number | null;
|
|
153
|
+
end: number | null;
|
|
154
|
+
length: number;
|
|
155
|
+
}) => any;
|
|
156
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
157
|
+
"onRow-hover-on"?: ((payload: {
|
|
158
|
+
offset: number;
|
|
159
|
+
}) => any) | undefined;
|
|
160
|
+
"onRow-hover-off"?: ((payload: {
|
|
161
|
+
offset: number;
|
|
162
|
+
}) => any) | undefined;
|
|
163
|
+
"onHex-hover-on"?: ((payload: {
|
|
164
|
+
index: number;
|
|
165
|
+
byte: number;
|
|
166
|
+
}) => any) | undefined;
|
|
167
|
+
"onHex-hover-off"?: ((payload: {
|
|
168
|
+
index: number;
|
|
169
|
+
byte: number;
|
|
170
|
+
}) => any) | undefined;
|
|
171
|
+
"onAscii-hover-on"?: ((payload: {
|
|
172
|
+
index: number;
|
|
173
|
+
byte: number;
|
|
174
|
+
}) => any) | undefined;
|
|
175
|
+
"onAscii-hover-off"?: ((payload: {
|
|
176
|
+
index: number;
|
|
177
|
+
byte: number;
|
|
178
|
+
}) => any) | undefined;
|
|
179
|
+
onUpdateVirtualData?: ((payload: VueHexWindowRequest) => any) | undefined;
|
|
180
|
+
onEdit?: ((payload: VueHexEditIntent) => any) | undefined;
|
|
181
|
+
"onByte-click"?: ((payload: {
|
|
182
|
+
index: number;
|
|
183
|
+
byte: number;
|
|
184
|
+
kind: "hex" | "ascii";
|
|
185
|
+
}) => any) | undefined;
|
|
186
|
+
"onSelection-change"?: ((payload: {
|
|
187
|
+
start: number | null;
|
|
188
|
+
end: number | null;
|
|
189
|
+
length: number;
|
|
190
|
+
}) => any) | undefined;
|
|
191
|
+
"onUpdate:windowOffset"?: ((value: number) => any) | undefined;
|
|
192
|
+
"onUpdate:modelValue"?: ((value: Uint8Array<ArrayBufferLike>) => any) | undefined;
|
|
193
|
+
"onUpdate:cursorLocation"?: ((value: number | null) => any) | undefined;
|
|
194
|
+
}>, {
|
|
195
|
+
editable: boolean;
|
|
196
|
+
cursor: boolean;
|
|
197
|
+
expandToContent: boolean;
|
|
198
|
+
uppercase: boolean;
|
|
199
|
+
isPrintable: VueHexPrintableCheck;
|
|
200
|
+
renderAscii: VueHexAsciiRenderer;
|
|
201
|
+
nonPrintableChar: string;
|
|
202
|
+
statusbar: "top" | "bottom" | null;
|
|
203
|
+
dataMode: "auto" | "buffer" | "window";
|
|
204
|
+
bytesPerRow: number;
|
|
205
|
+
overscan: number;
|
|
206
|
+
showChunkNavigator: boolean;
|
|
207
|
+
chunkNavigatorPlacement: "left" | "right" | "top" | "bottom";
|
|
208
|
+
statusbarLayout: VueHexStatusBarLayout | null;
|
|
209
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
210
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
211
|
+
declare const _default: typeof __VLS_export;
|
|
212
|
+
export default _default;
|
|
213
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
214
|
+
new (): {
|
|
215
|
+
$slots: S;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type ChunkNavigatorPlacement = "left" | "right" | "top" | "bottom";
|
|
2
|
+
export interface ChunkDescriptor {
|
|
3
|
+
index: number;
|
|
4
|
+
label: string;
|
|
5
|
+
range: string;
|
|
6
|
+
}
|
|
7
|
+
interface VueHexChunkNavigatorProps {
|
|
8
|
+
show?: boolean;
|
|
9
|
+
placement?: ChunkNavigatorPlacement;
|
|
10
|
+
chunks: ChunkDescriptor[];
|
|
11
|
+
activeIndex: number;
|
|
12
|
+
rootClassExtra?: string[];
|
|
13
|
+
viewerClassExtra?: string[];
|
|
14
|
+
expandToContent?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare var __VLS_1: {
|
|
17
|
+
chunks: ChunkDescriptor[];
|
|
18
|
+
activeIndex: number;
|
|
19
|
+
}, __VLS_3: {
|
|
20
|
+
chunk: ChunkDescriptor;
|
|
21
|
+
active: boolean;
|
|
22
|
+
select: () => void;
|
|
23
|
+
}, __VLS_5: {};
|
|
24
|
+
type __VLS_Slots = {} & {
|
|
25
|
+
'chunk-navigator-header'?: (props: typeof __VLS_1) => any;
|
|
26
|
+
} & {
|
|
27
|
+
'chunk-navigator-item'?: (props: typeof __VLS_3) => any;
|
|
28
|
+
} & {
|
|
29
|
+
default?: (props: typeof __VLS_5) => any;
|
|
30
|
+
};
|
|
31
|
+
declare const __VLS_base: import("vue").DefineComponent<VueHexChunkNavigatorProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
32
|
+
select: (index: number) => any;
|
|
33
|
+
}, string, import("vue").PublicProps, Readonly<VueHexChunkNavigatorProps> & Readonly<{
|
|
34
|
+
onSelect?: ((index: number) => any) | undefined;
|
|
35
|
+
}>, {
|
|
36
|
+
show: boolean;
|
|
37
|
+
placement: ChunkNavigatorPlacement;
|
|
38
|
+
rootClassExtra: string[];
|
|
39
|
+
viewerClassExtra: string[];
|
|
40
|
+
expandToContent: boolean;
|
|
41
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
43
|
+
declare const _default: typeof __VLS_export;
|
|
44
|
+
export default _default;
|
|
45
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
46
|
+
new (): {
|
|
47
|
+
$slots: S;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { VueHexAsciiRenderer, VueHexEditorColumn, VueHexEditorMode, VueHexPrintableCheck, VueHexStatusBarLayout } from "./vuehex-api";
|
|
2
|
+
type StatusBarPlacement = "top" | "bottom";
|
|
3
|
+
interface VueHexStatusBarProps {
|
|
4
|
+
placement: StatusBarPlacement;
|
|
5
|
+
layout?: VueHexStatusBarLayout | null;
|
|
6
|
+
uppercase?: boolean;
|
|
7
|
+
isPrintable?: VueHexPrintableCheck;
|
|
8
|
+
renderAscii?: VueHexAsciiRenderer;
|
|
9
|
+
nonPrintableChar?: string;
|
|
10
|
+
selectionRange: {
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
} | null;
|
|
14
|
+
selectionCount: number;
|
|
15
|
+
editable?: boolean;
|
|
16
|
+
editorMode?: VueHexEditorMode | null;
|
|
17
|
+
editorColumn?: VueHexEditorColumn | null;
|
|
18
|
+
totalBytes?: number;
|
|
19
|
+
}
|
|
20
|
+
type VueHexHoverEvent = "row-hover-on" | "row-hover-off" | "hex-hover-on" | "hex-hover-off" | "ascii-hover-on" | "ascii-hover-off";
|
|
21
|
+
type VueHexHoverEmit = (event: VueHexHoverEvent, payload: unknown) => void;
|
|
22
|
+
declare var __VLS_2: "statusbar-left" | "statusbar-middle" | "statusbar-right", __VLS_3: {}, __VLS_6: "statusbar-left" | "statusbar-middle" | "statusbar-right", __VLS_7: {}, __VLS_10: "statusbar-left" | "statusbar-middle" | "statusbar-right", __VLS_11: {};
|
|
23
|
+
type __VLS_Slots = {} & {
|
|
24
|
+
[K in NonNullable<typeof __VLS_2>]?: (props: typeof __VLS_3) => any;
|
|
25
|
+
} & {
|
|
26
|
+
[K in NonNullable<typeof __VLS_6>]?: (props: typeof __VLS_7) => any;
|
|
27
|
+
} & {
|
|
28
|
+
[K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
|
|
29
|
+
};
|
|
30
|
+
declare const __VLS_base: import("vue").DefineComponent<VueHexStatusBarProps, {
|
|
31
|
+
handleHoverEvent: VueHexHoverEmit;
|
|
32
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<VueHexStatusBarProps> & Readonly<{}>, {
|
|
33
|
+
editable: boolean;
|
|
34
|
+
layout: VueHexStatusBarLayout | null;
|
|
35
|
+
uppercase: boolean;
|
|
36
|
+
isPrintable: VueHexPrintableCheck;
|
|
37
|
+
renderAscii: VueHexAsciiRenderer;
|
|
38
|
+
nonPrintableChar: string;
|
|
39
|
+
selectionRange: {
|
|
40
|
+
start: number;
|
|
41
|
+
end: number;
|
|
42
|
+
} | null;
|
|
43
|
+
selectionCount: number;
|
|
44
|
+
editorMode: VueHexEditorMode | null;
|
|
45
|
+
editorColumn: VueHexEditorColumn | null;
|
|
46
|
+
totalBytes: number;
|
|
47
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
48
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
49
|
+
declare const _default: typeof __VLS_export;
|
|
50
|
+
export default _default;
|
|
51
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
52
|
+
new (): {
|
|
53
|
+
$slots: S;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* A UI-facing description of a single chunk in the chunk navigator.
|
|
4
|
+
*/
|
|
5
|
+
export interface VueHexChunkDescriptor {
|
|
6
|
+
index: number;
|
|
7
|
+
label: string;
|
|
8
|
+
range: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Inputs required to compute chunk boundaries and navigator metadata.
|
|
12
|
+
*/
|
|
13
|
+
interface UseChunkingOptions {
|
|
14
|
+
/** Whether the chunk navigator UI is enabled/visible. */
|
|
15
|
+
showNavigator: ComputedRef<boolean>;
|
|
16
|
+
/** Total bytes in the backing data source. */
|
|
17
|
+
totalBytes: ComputedRef<number>;
|
|
18
|
+
/** Bytes per rendered row (used to convert between bytes and rows). */
|
|
19
|
+
bytesPerRow: ComputedRef<number>;
|
|
20
|
+
/** Measured row height in pixels (used to cap the virtual scroll height). */
|
|
21
|
+
rowHeightValue: ComputedRef<number>;
|
|
22
|
+
/** Max virtual height in pixels allowed before chunking is enabled. */
|
|
23
|
+
maxVirtualHeight: ComputedRef<number>;
|
|
24
|
+
/** Casing preference used for chunk range labels. */
|
|
25
|
+
uppercase: ComputedRef<boolean>;
|
|
26
|
+
}
|
|
27
|
+
export interface UseChunkingResult {
|
|
28
|
+
/** Absolute start row (0-based) of the active chunk. */
|
|
29
|
+
chunkStartRow: Ref<number>;
|
|
30
|
+
/** Total number of rows required to render `totalBytes` at `bytesPerRow`. */
|
|
31
|
+
totalRows: ComputedRef<number>;
|
|
32
|
+
/** Maximum rows per chunk derived from `maxVirtualHeight / rowHeight`. */
|
|
33
|
+
chunkRowCapacity: ComputedRef<number>;
|
|
34
|
+
/** True when chunking is active (total rows exceed capacity). */
|
|
35
|
+
isChunking: ComputedRef<boolean>;
|
|
36
|
+
/** Total number of chunks available for the current dataset. */
|
|
37
|
+
chunkCount: ComputedRef<number>;
|
|
38
|
+
/** 0-based chunk index that contains `chunkStartRow`. */
|
|
39
|
+
activeChunkIndex: ComputedRef<number>;
|
|
40
|
+
/** Number of rows available within the active chunk. */
|
|
41
|
+
chunkRowCount: ComputedRef<number>;
|
|
42
|
+
/** Pixel height of the active chunk (`chunkRowCount * rowHeightValue`). */
|
|
43
|
+
chunkHeight: ComputedRef<number>;
|
|
44
|
+
/** Items suitable for a chunk navigator UI (empty when disabled). */
|
|
45
|
+
chunkItems: ComputedRef<VueHexChunkDescriptor[]>;
|
|
46
|
+
/** Normalizes `chunkStartRow` to valid bounds and chunk boundaries. */
|
|
47
|
+
clampChunkStartToBounds: () => void;
|
|
48
|
+
/** Ensures the chunk containing `row` is active; returns true if it changed. */
|
|
49
|
+
ensureChunkForRow: (row: number) => boolean;
|
|
50
|
+
/** Selects the chunk by 0-based index; returns true if it changed. */
|
|
51
|
+
selectChunk: (index: number) => boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Provides chunking state + helpers for very large virtualized hex views.
|
|
55
|
+
*
|
|
56
|
+
* Chunking is used to cap the maximum virtual scroll height: the full data is
|
|
57
|
+
* split into fixed-size row ranges (chunks), and the UI renders/scrolls within
|
|
58
|
+
* the currently active chunk.
|
|
59
|
+
*
|
|
60
|
+
* Used by VueHex to keep scroll containers manageable for extremely large inputs,
|
|
61
|
+
* and to power the optional chunk navigator UI.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useChunking(options: UseChunkingOptions): UseChunkingResult;
|
|
64
|
+
export type UseChunkingReturn = ReturnType<typeof useChunking>;
|
|
65
|
+
export type ChunkStartRowRef = Ref<number>;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
|
2
|
+
export interface CursorOptions {
|
|
3
|
+
/** Master enable toggle for cursor behaviors and DOM highlighting. */
|
|
4
|
+
enabled: ComputedRef<boolean>;
|
|
5
|
+
/** When true, scrolling/virtualization is disabled so cursor scrolling is skipped. */
|
|
6
|
+
isExpandToContent: ComputedRef<boolean>;
|
|
7
|
+
/** Scroll container element (focus + scrollTop updates). */
|
|
8
|
+
containerEl: Ref<HTMLDivElement | undefined>;
|
|
9
|
+
/** Table body element containing the rendered cells (for class toggling). */
|
|
10
|
+
tbodyEl: Ref<HTMLTableSectionElement | undefined>;
|
|
11
|
+
/** Current rendered markup (used to know when to re-apply cursor highlights). */
|
|
12
|
+
markup: ShallowRef<string>;
|
|
13
|
+
/** Total bytes in the backing data source (for clamping). */
|
|
14
|
+
totalBytes: ComputedRef<number>;
|
|
15
|
+
/** Bytes per row (for row math during navigation). */
|
|
16
|
+
bytesPerRow: ComputedRef<number>;
|
|
17
|
+
/** Row height in pixels (for scroll math). */
|
|
18
|
+
rowHeightValue: ComputedRef<number>;
|
|
19
|
+
/** Visible rows in the viewport (used to keep cursor in view). */
|
|
20
|
+
viewportRows: ComputedRef<number>;
|
|
21
|
+
/** Current chunk start row (for relative scroll math within a chunk). */
|
|
22
|
+
chunkStartRow: Ref<number>;
|
|
23
|
+
/** Ensures the chunk containing a row is active; returns true if it changed. */
|
|
24
|
+
ensureChunkForRow: (row: number) => boolean;
|
|
25
|
+
/** Schedules a re-evaluation of the rendered window after scroll/chunk changes. */
|
|
26
|
+
scheduleWindowEvaluation: () => void;
|
|
27
|
+
/** External cursor location model (0-based byte index), or null. */
|
|
28
|
+
cursorLocation: Ref<number | null>;
|
|
29
|
+
/** Requests scrolling such that an absolute byte offset is visible. */
|
|
30
|
+
scrollToByte: (offset: number) => void;
|
|
31
|
+
}
|
|
32
|
+
export interface CursorResult {
|
|
33
|
+
/**
|
|
34
|
+
* Normalized cursor location (byte index) or null when cursor is disabled/unset.
|
|
35
|
+
*/
|
|
36
|
+
cursorLocation: ComputedRef<number | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Updates the cursor location (clamped to the valid byte range).
|
|
39
|
+
*/
|
|
40
|
+
setCursorLocation: (index: number | null) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Manages the interactive cursor for VueHex.
|
|
44
|
+
*
|
|
45
|
+
* Why it exists:
|
|
46
|
+
* - Centralizes keyboard + pointer navigation for the "active byte".
|
|
47
|
+
* - Applies/removes cursor highlight classes to both hex and ASCII cells.
|
|
48
|
+
* - Ensures the active byte stays visible within the current scroll window.
|
|
49
|
+
*
|
|
50
|
+
* How it is used:
|
|
51
|
+
* - VueHex wires table/container refs + event handlers into this composable.
|
|
52
|
+
* - When the cursor changes, it updates the `cursorLocation` model value and
|
|
53
|
+
* mutates DOM classes inside the rendered `tbody` markup.
|
|
54
|
+
* - For large datasets, it works with chunking/window virtualization via
|
|
55
|
+
* `ensureChunkForRow` and `scheduleWindowEvaluation`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function useCursor(options: CursorOptions): CursorResult;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from "vue";
|
|
2
|
+
export interface UseDataModeOptions {
|
|
3
|
+
/** `dataMode` prop (or undefined) controlling buffer/window/auto behavior. */
|
|
4
|
+
dataMode: Ref<"auto" | "buffer" | "window" | undefined>;
|
|
5
|
+
/** When true, VueHex expands to content and never uses windowing. */
|
|
6
|
+
isExpandToContent: ComputedRef<boolean>;
|
|
7
|
+
/** Current window offset in bytes (used by the host in windowed mode). */
|
|
8
|
+
windowOffset: ComputedRef<number>;
|
|
9
|
+
/** Total size of the backing source (may be larger than provided data). */
|
|
10
|
+
totalSize: Ref<number | undefined>;
|
|
11
|
+
/** Current length of the provided data (e.g. modelValue.length). */
|
|
12
|
+
dataLength: ComputedRef<number>;
|
|
13
|
+
}
|
|
14
|
+
export interface UseDataModeResult {
|
|
15
|
+
/** Normalized mode after applying defaults and validation. */
|
|
16
|
+
normalizedMode: ComputedRef<"auto" | "buffer" | "window">;
|
|
17
|
+
/** True when VueHex should request/operate on a moving window of data. */
|
|
18
|
+
isWindowed: ComputedRef<boolean>;
|
|
19
|
+
/** True when VueHex owns the full buffer locally (non-windowed). */
|
|
20
|
+
isSelfManaged: ComputedRef<boolean>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Composable that centralizes data mode detection logic.
|
|
24
|
+
*
|
|
25
|
+
* Why it exists:
|
|
26
|
+
* - VueHex supports both "buffer" mode (all bytes provided) and "window" mode
|
|
27
|
+
* (only a slice provided; the host is responsible for loading more).
|
|
28
|
+
* - This composable keeps the mode decision logic in one place so the rest of
|
|
29
|
+
* the component can branch on `isWindowed` / `isSelfManaged`.
|
|
30
|
+
*
|
|
31
|
+
* How it is used:
|
|
32
|
+
* - VueHex passes `dataMode`, `expandToContent`, `totalSize`, and the current
|
|
33
|
+
* `modelValue.length`.
|
|
34
|
+
* - In `auto` mode, it treats `totalSize > modelValue.length` as windowed.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useDataMode(options: UseDataModeOptions): UseDataModeResult;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
|
2
|
+
import type { VueHexEditIntent, VueHexEditorColumn, VueHexEditorMode } from "../vuehex-api";
|
|
3
|
+
export interface EditingOptions {
|
|
4
|
+
enabled: ComputedRef<boolean>;
|
|
5
|
+
containerEl: Ref<HTMLDivElement | undefined>;
|
|
6
|
+
tbodyEl: Ref<HTMLTableSectionElement | undefined>;
|
|
7
|
+
markup: ShallowRef<string>;
|
|
8
|
+
uppercase: ComputedRef<boolean>;
|
|
9
|
+
cursorIndex: ComputedRef<number | null>;
|
|
10
|
+
setCursorIndex: (index: number | null) => void;
|
|
11
|
+
selectionRange: ComputedRef<{
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
} | null>;
|
|
15
|
+
clearSelection: () => void;
|
|
16
|
+
copySelectionToClipboard: () => Promise<boolean>;
|
|
17
|
+
totalBytes: ComputedRef<number>;
|
|
18
|
+
isSelfManagedData: ComputedRef<boolean>;
|
|
19
|
+
getSelfManagedBytes: () => Uint8Array;
|
|
20
|
+
setSelfManagedBytes: (bytes: Uint8Array) => void;
|
|
21
|
+
emitEdit: (intent: VueHexEditIntent) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface EditingResult {
|
|
24
|
+
activeColumn: Ref<VueHexEditorColumn>;
|
|
25
|
+
editorMode: Ref<VueHexEditorMode>;
|
|
26
|
+
resetPendingHexInput: () => void;
|
|
27
|
+
}
|
|
28
|
+
export declare function useEditing(options: EditingOptions): EditingResult;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
|
2
|
+
import type { VueHexAsciiRenderer, VueHexCellClassResolver, VueHexPrintableCheck, VueHexWindow, VueHexWindowRequest } from "../vuehex-api";
|
|
3
|
+
export interface HexWindowOptions {
|
|
4
|
+
/** Scroll container element (scrollTop source of truth). */
|
|
5
|
+
containerEl: Ref<HTMLDivElement | undefined>;
|
|
6
|
+
/** Table body element where HTML markup is injected. */
|
|
7
|
+
tbodyEl: Ref<HTMLTableSectionElement | undefined>;
|
|
8
|
+
/** Bytes per row (affects layout + offset-to-row conversions). */
|
|
9
|
+
bytesPerRow: ComputedRef<number>;
|
|
10
|
+
/** Measured row height (null until measured). */
|
|
11
|
+
rowHeight: Ref<number | null>;
|
|
12
|
+
/** Row height value used for calculations (fallback when not measured). */
|
|
13
|
+
rowHeightValue: ComputedRef<number>;
|
|
14
|
+
/** Number of visible rows in the viewport. */
|
|
15
|
+
viewportRows: ComputedRef<number>;
|
|
16
|
+
/** Rows to render above/below viewport to reduce flicker. */
|
|
17
|
+
overscanRows: ComputedRef<number>;
|
|
18
|
+
/** Active chunk start row (0-based). */
|
|
19
|
+
chunkStartRow: Ref<number>;
|
|
20
|
+
/** Total rows available within the active chunk. */
|
|
21
|
+
chunkRowCount: ComputedRef<number>;
|
|
22
|
+
/** Total bytes in the backing data source. */
|
|
23
|
+
totalBytes: ComputedRef<number>;
|
|
24
|
+
/** Total bytes that can actually be requested from the host (excludes the editable EOF ghost). */
|
|
25
|
+
requestTotalBytes: ComputedRef<number>;
|
|
26
|
+
/** When true, renders a single EOF ghost byte at index == requestTotalBytes. */
|
|
27
|
+
includeGhostEnd?: ComputedRef<boolean>;
|
|
28
|
+
/** True when VueHex treats the provided data as the full buffer (no windowing). */
|
|
29
|
+
isSelfManagedData: ComputedRef<boolean>;
|
|
30
|
+
/** Two-way model tracking the current absolute offset (used for buffer mode scroll sync). */
|
|
31
|
+
windowOffset: Ref<number>;
|
|
32
|
+
/** Ensures the chunk containing a row is active; returns true if it changed. */
|
|
33
|
+
ensureChunkForRow: (row: number) => boolean;
|
|
34
|
+
/** Normalizes `chunkStartRow` to valid bounds/chunk boundaries. */
|
|
35
|
+
clampChunkStartToBounds: () => void;
|
|
36
|
+
/** Reads the latest window state (offset + data) from the host/component. */
|
|
37
|
+
getWindowState: () => VueHexWindow;
|
|
38
|
+
/** Casing preference for hex rendering. */
|
|
39
|
+
getUppercase: () => boolean;
|
|
40
|
+
/** Determines whether bytes are printable for ASCII rendering. */
|
|
41
|
+
getPrintableChecker: () => VueHexPrintableCheck;
|
|
42
|
+
/** Converts a byte into an ASCII glyph (when printable). */
|
|
43
|
+
getAsciiRenderer: () => VueHexAsciiRenderer;
|
|
44
|
+
/** Optional class resolver for per-cell highlighting. */
|
|
45
|
+
getCellClassResolver: () => VueHexCellClassResolver | undefined;
|
|
46
|
+
/** Replacement character for non-printable bytes. */
|
|
47
|
+
getNonPrintableChar: () => string;
|
|
48
|
+
/** Current selection range (inclusive) for embedding selection classes. */
|
|
49
|
+
getSelectionRange: () => {
|
|
50
|
+
start: number;
|
|
51
|
+
end: number;
|
|
52
|
+
} | null;
|
|
53
|
+
/** Issues a window request to the host (used only in windowed mode). */
|
|
54
|
+
requestWindow: (request: VueHexWindowRequest) => void;
|
|
55
|
+
/** Clears hover state when markup changes. */
|
|
56
|
+
clearHoverState: () => void;
|
|
57
|
+
}
|
|
58
|
+
export interface HexWindowResult {
|
|
59
|
+
/** Rendered HTML for the visible table slice (consumed via `v-html`). */
|
|
60
|
+
markup: ShallowRef<string>;
|
|
61
|
+
/** Normalized bytes backing the current rendered/windowed slice. */
|
|
62
|
+
normalizedBytes: ShallowRef<Uint8Array<ArrayBufferLike>>;
|
|
63
|
+
/** HTML-safe replacement for non-printable ASCII characters. */
|
|
64
|
+
fallbackAsciiChar: ShallowRef<string>;
|
|
65
|
+
/** Absolute start row of the currently rendered markup slice. */
|
|
66
|
+
renderStartRow: Ref<number>;
|
|
67
|
+
/** Count of rows currently rendered from `normalizedBytes`. */
|
|
68
|
+
renderedRows: ComputedRef<number>;
|
|
69
|
+
/** Absolute start row of the current externally-provided window (`windowOffset`). */
|
|
70
|
+
startRow: ComputedRef<number>;
|
|
71
|
+
/** Schedules a recalculation of visible rows and window requests. */
|
|
72
|
+
scheduleWindowEvaluation: () => void;
|
|
73
|
+
/** Scroll handler for the container; coalesces work via RAF. */
|
|
74
|
+
handleScroll: () => void;
|
|
75
|
+
/** Immediately scrolls to ensure an absolute byte offset is visible. */
|
|
76
|
+
scrollToByte: (offset: number) => void;
|
|
77
|
+
/** Queues a scroll-to-offset to be applied on the next evaluation tick. */
|
|
78
|
+
queueScrollToOffset: (offset: number) => void;
|
|
79
|
+
/** Syncs internal buffers from the current external window state. */
|
|
80
|
+
updateFromWindowState: () => void;
|
|
81
|
+
/** Regenerates markup for the current visible slice. */
|
|
82
|
+
updateRenderedSlice: () => void;
|
|
83
|
+
/** Measures row height from the DOM when possible (used for scroll math). */
|
|
84
|
+
measureRowHeight: () => void;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Owns the virtualized "rendered window" for VueHex.
|
|
88
|
+
*
|
|
89
|
+
* Why it exists:
|
|
90
|
+
* - Generates the table body's HTML markup (via `v-html`) for only the rows that
|
|
91
|
+
* should be visible (plus overscan), rather than rendering the full dataset.
|
|
92
|
+
* - Keeps scrolling smooth by translating scrollTop into a rendered row slice.
|
|
93
|
+
* - Coordinates external window requests for huge datasets ("windowed mode")
|
|
94
|
+
* while also supporting "buffer mode" where all bytes are locally available.
|
|
95
|
+
*
|
|
96
|
+
* How it is used:
|
|
97
|
+
* - VueHex provides refs to the scroll container and `tbody`, plus rendering
|
|
98
|
+
* preferences (bytesPerRow, casing, ASCII rendering, class resolvers).
|
|
99
|
+
* - `handleScroll` and `scheduleWindowEvaluation` are called on scroll to update
|
|
100
|
+
* markup and to request more data when needed.
|
|
101
|
+
* - Works in tandem with chunking (`chunkStartRow`, `chunkRowCount`) to keep the
|
|
102
|
+
* virtual scroll height bounded for very large inputs.
|
|
103
|
+
*/
|
|
104
|
+
export declare function useHexWindow(options: HexWindowOptions): HexWindowResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* Typed emitter contract for hover events forwarded by VueHex.
|
|
4
|
+
*
|
|
5
|
+
* Why it exists: keeps the composable decoupled from the Vue component instance
|
|
6
|
+
* while still allowing consumers to observe hover state.
|
|
7
|
+
*/
|
|
8
|
+
type HoverEmit = (event: "row-hover-on" | "row-hover-off" | "hex-hover-on" | "hex-hover-off" | "ascii-hover-on" | "ascii-hover-off", payload: unknown) => void;
|
|
9
|
+
export interface HoverLinkingOptions {
|
|
10
|
+
/** Event emitter from VueHex; hover events are forwarded to component consumers. */
|
|
11
|
+
emit: HoverEmit;
|
|
12
|
+
/** Table body element containing rendered cells (used for class toggling). */
|
|
13
|
+
tbodyEl: Ref<HTMLTableSectionElement | undefined>;
|
|
14
|
+
}
|
|
15
|
+
export interface HoverLinkingResult {
|
|
16
|
+
/** Pointer-over handler (attach to the table body). */
|
|
17
|
+
handlePointerOver: (event: PointerEvent) => void;
|
|
18
|
+
/** Pointer-out handler (attach to the table body). */
|
|
19
|
+
handlePointerOut: (event: PointerEvent) => void;
|
|
20
|
+
/** Clears any active hover state and linked highlighting. */
|
|
21
|
+
clearHoverState: () => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Links hover state between rows, hex cells, and ASCII cells.
|
|
25
|
+
*
|
|
26
|
+
* Why it exists:
|
|
27
|
+
* - Keeps hex/ASCII columns visually synchronized on hover.
|
|
28
|
+
* - Emits semantic hover events (row/hex/ascii enter/leave) for consumers.
|
|
29
|
+
* - Debounces DOM class updates to avoid excessive work during pointer movement.
|
|
30
|
+
*
|
|
31
|
+
* How it is used:
|
|
32
|
+
* - VueHex forwards `pointerover`/`pointerout` events from the rendered `tbody`.
|
|
33
|
+
* - The composable reads `data-*` attributes embedded in the markup to determine
|
|
34
|
+
* which row/cell is under the pointer and emits matching events.
|
|
35
|
+
* - `clearHoverState` is called when the pointer leaves the table or when the
|
|
36
|
+
* rendered markup changes.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useHoverLinking(options: HoverLinkingOptions): HoverLinkingResult;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
|
2
|
+
import type { VueHexAsciiRenderer, VueHexPrintableCheck, VueHexSelectionDataProvider } from "../vuehex-api";
|
|
3
|
+
export interface SelectionOptions {
|
|
4
|
+
/** Scroll container element (focus + pointer event wiring). */
|
|
5
|
+
containerEl: Ref<HTMLDivElement | undefined>;
|
|
6
|
+
/** Table body element containing rendered cells (used for event delegation). */
|
|
7
|
+
tbodyEl: Ref<HTMLTableSectionElement | undefined>;
|
|
8
|
+
/** Current rendered markup; changes trigger re-sync of selection classes. */
|
|
9
|
+
markup: ShallowRef<string>;
|
|
10
|
+
/** Optional user-provided selection data provider (used for copy). */
|
|
11
|
+
getSelectionDataProp: () => VueHexSelectionDataProvider | undefined;
|
|
12
|
+
/** True when VueHex owns the full data buffer locally. */
|
|
13
|
+
isSelfManagedData: ComputedRef<boolean>;
|
|
14
|
+
/** Total bytes in the backing data source (for clamping). */
|
|
15
|
+
totalBytes: ComputedRef<number>;
|
|
16
|
+
/** Returns the locally-managed bytes when in self-managed mode. */
|
|
17
|
+
getSelfManagedBytes: () => Uint8Array;
|
|
18
|
+
/** Casing preference used by renderer helpers. */
|
|
19
|
+
getUppercase: () => boolean;
|
|
20
|
+
/** Determines whether bytes are printable for ASCII rendering. */
|
|
21
|
+
getPrintableChecker: () => VueHexPrintableCheck;
|
|
22
|
+
/** Converts a byte into an ASCII glyph (when printable). */
|
|
23
|
+
getAsciiRenderer: () => VueHexAsciiRenderer;
|
|
24
|
+
/** Replacement character for non-printable bytes. */
|
|
25
|
+
getNonPrintableChar: () => string;
|
|
26
|
+
/** Forces markup regeneration so selection CSS classes are embedded. */
|
|
27
|
+
updateRenderedSlice: () => void;
|
|
28
|
+
/**
|
|
29
|
+
* When true, selection can only be started/updated via Shift+click.
|
|
30
|
+
*
|
|
31
|
+
* Why it exists: in cursor/editable modes, normal clicks should move the cursor
|
|
32
|
+
* without implicitly selecting a single byte.
|
|
33
|
+
*/
|
|
34
|
+
requireShiftToSelect?: ComputedRef<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Optional cursor index used as the anchor when shift-clicking with no existing selection.
|
|
37
|
+
*
|
|
38
|
+
* Why it exists: supports common UX where click moves cursor, then shift-click
|
|
39
|
+
* selects the range from the cursor to the clicked byte.
|
|
40
|
+
*/
|
|
41
|
+
cursorIndex?: ComputedRef<number | null>;
|
|
42
|
+
/** Optional emitter for click events on individual bytes. */
|
|
43
|
+
emitByteClick?: (payload: {
|
|
44
|
+
index: number;
|
|
45
|
+
byte: number;
|
|
46
|
+
kind: "hex" | "ascii";
|
|
47
|
+
}) => void;
|
|
48
|
+
/** Optional emitter for selection range changes. */
|
|
49
|
+
emitSelectionChange?: (payload: {
|
|
50
|
+
start: number | null;
|
|
51
|
+
end: number | null;
|
|
52
|
+
length: number;
|
|
53
|
+
}) => void;
|
|
54
|
+
}
|
|
55
|
+
export interface SelectionResult {
|
|
56
|
+
/**
|
|
57
|
+
* Function used to retrieve selected bytes for copy/consumer APIs.
|
|
58
|
+
* Null when selection is disabled.
|
|
59
|
+
*/
|
|
60
|
+
selectionDataProvider: ComputedRef<VueHexSelectionDataProvider | null>;
|
|
61
|
+
/** True when selection is active/enabled. */
|
|
62
|
+
selectionEnabled: ComputedRef<boolean>;
|
|
63
|
+
/** Ordered selection range (inclusive), or null when there is no selection. */
|
|
64
|
+
selectionRange: ComputedRef<{
|
|
65
|
+
start: number;
|
|
66
|
+
end: number;
|
|
67
|
+
} | null>;
|
|
68
|
+
/** Number of selected bytes. */
|
|
69
|
+
selectionCount: ComputedRef<number>;
|
|
70
|
+
/** Clears any active selection (no-op if none). */
|
|
71
|
+
clearSelection: () => void;
|
|
72
|
+
/** Copies the current selection to the clipboard (no-op if none). */
|
|
73
|
+
copySelectionToClipboard: () => Promise<boolean>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Provides mouse/pointer-driven selection for VueHex.
|
|
77
|
+
*
|
|
78
|
+
* Why it exists:
|
|
79
|
+
* - Tracks anchor/focus to form a stable selection range across interactions.
|
|
80
|
+
* - Supports selection in either the hex or ASCII column while keeping indices aligned.
|
|
81
|
+
* - Emits selection/click events and triggers markup regeneration so selection CSS
|
|
82
|
+
* classes are embedded in the rendered HTML.
|
|
83
|
+
*
|
|
84
|
+
* How it is used:
|
|
85
|
+
* - VueHex passes DOM refs (`containerEl`, `tbodyEl`) and the current markup ref.
|
|
86
|
+
* - The composable attaches pointer listeners and updates selection state.
|
|
87
|
+
* - When selection changes, it requests a re-render via `updateRenderedSlice`.
|
|
88
|
+
* - For clipboard/copy workflows, it exposes a `selectionDataProvider` that is
|
|
89
|
+
* either user-supplied (`getSelectionDataProp`) or derived from self-managed data.
|
|
90
|
+
*/
|
|
91
|
+
export declare function useSelection(options: SelectionOptions): SelectionResult;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from "vue";
|
|
2
|
+
export interface UseVueHexThemeOptions {
|
|
3
|
+
/** Theme token provided by the consumer (prop). */
|
|
4
|
+
theme: Ref<string | null | undefined>;
|
|
5
|
+
/** True when VueHex expands to fit content (no virtualization). */
|
|
6
|
+
isExpandToContent: ComputedRef<boolean>;
|
|
7
|
+
/** True when selection features are enabled; affects container classes. */
|
|
8
|
+
selectionEnabled: ComputedRef<boolean>;
|
|
9
|
+
/** Status bar placement prop; null/undefined means hidden. */
|
|
10
|
+
statusbar: Ref<"top" | "bottom" | null | undefined>;
|
|
11
|
+
}
|
|
12
|
+
export interface UseVueHexThemeResult {
|
|
13
|
+
/** Canonical theme key (kebab-case), or null when no explicit theme is set. */
|
|
14
|
+
themeKey: ComputedRef<string | null>;
|
|
15
|
+
/** Theme classes applied to the component root and related wrappers. */
|
|
16
|
+
themeClass: ComputedRef<string[]>;
|
|
17
|
+
/** Classes applied to the scroll container element. */
|
|
18
|
+
containerClass: ComputedRef<string[]>;
|
|
19
|
+
/** Extra classes forwarded to the chunk navigator root wrapper. */
|
|
20
|
+
rootClassExtra: ComputedRef<string[]>;
|
|
21
|
+
/** True when the status bar should be rendered. */
|
|
22
|
+
showStatusBar: ComputedRef<boolean>;
|
|
23
|
+
/** Status bar placement, or null when the status bar is hidden. */
|
|
24
|
+
statusBarPlacement: ComputedRef<"top" | "bottom" | null>;
|
|
25
|
+
/** Extra classes forwarded to the chunk navigator viewer wrapper. */
|
|
26
|
+
viewerClassExtra: ComputedRef<string[]>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Centralizes theme + class computations for VueHex.
|
|
30
|
+
*
|
|
31
|
+
* Why it exists:
|
|
32
|
+
* - VueHex derives multiple class lists (container/root/viewer) from the theme,
|
|
33
|
+
* interactive state, and status bar placement.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useVueHexTheme(options: UseVueHexThemeOptions): UseVueHexThemeResult;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes the currently loaded window of bytes, including the absolute offset into the full data set.
|
|
3
|
+
*/
|
|
4
|
+
export interface VueHexWindow {
|
|
5
|
+
offset: number;
|
|
6
|
+
data: Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Payload emitted when VueHex needs a new slice of bytes from the host application.
|
|
10
|
+
*/
|
|
11
|
+
export interface VueHexWindowRequest {
|
|
12
|
+
offset: number;
|
|
13
|
+
length: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Built-in theme keys supported by VueHex out of the box.
|
|
17
|
+
*
|
|
18
|
+
* Custom themes are also supported by passing any token to the `theme` prop
|
|
19
|
+
* (VueHex emits `.vuehex-theme-<token>`).
|
|
20
|
+
*/
|
|
21
|
+
export declare const VUE_HEX_BUILTIN_THEME_KEYS: readonly ["auto", "light", "dark", "terminal", "sunset"];
|
|
22
|
+
/** Union type of the built-in theme keys. */
|
|
23
|
+
export type VueHexBuiltinThemeKey = (typeof VUE_HEX_BUILTIN_THEME_KEYS)[number];
|
|
24
|
+
/**
|
|
25
|
+
* Function that determines whether a given byte should be treated as printable ASCII for the viewer.
|
|
26
|
+
*/
|
|
27
|
+
export type VueHexPrintableCheck = (byte: number) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Function that renders a printable byte into its display string representation.
|
|
30
|
+
*/
|
|
31
|
+
export type VueHexAsciiRenderer = (byte: number) => string;
|
|
32
|
+
/**
|
|
33
|
+
* Fetches raw bytes for the given inclusive selection range.
|
|
34
|
+
*/
|
|
35
|
+
export type VueHexSelectionDataProvider = (selectionStart: number, selectionEnd: number) => Uint8Array;
|
|
36
|
+
/** Which column the editor is currently targeting. */
|
|
37
|
+
export type VueHexEditorColumn = "hex" | "ascii";
|
|
38
|
+
/** Editing mode: overwrite replaces bytes; insert grows the buffer. */
|
|
39
|
+
export type VueHexEditorMode = "overwrite" | "insert";
|
|
40
|
+
/**
|
|
41
|
+
* Single edit intent emitted by VueHex when `editable` is enabled.
|
|
42
|
+
*
|
|
43
|
+
* Consumers that own the backing buffer should apply these intents to their
|
|
44
|
+
* data source and feed the updated bytes back via `v-model`.
|
|
45
|
+
*/
|
|
46
|
+
export type VueHexEditIntent = {
|
|
47
|
+
kind: "overwrite-byte";
|
|
48
|
+
index: number;
|
|
49
|
+
value: number;
|
|
50
|
+
column: VueHexEditorColumn;
|
|
51
|
+
} | {
|
|
52
|
+
kind: "insert-byte";
|
|
53
|
+
index: number;
|
|
54
|
+
value: number;
|
|
55
|
+
column: VueHexEditorColumn;
|
|
56
|
+
} | {
|
|
57
|
+
kind: "overwrite-bytes";
|
|
58
|
+
index: number;
|
|
59
|
+
values: number[];
|
|
60
|
+
column: VueHexEditorColumn;
|
|
61
|
+
} | {
|
|
62
|
+
kind: "insert-bytes";
|
|
63
|
+
index: number;
|
|
64
|
+
values: number[];
|
|
65
|
+
column: VueHexEditorColumn;
|
|
66
|
+
} | {
|
|
67
|
+
kind: "delete-byte";
|
|
68
|
+
index: number;
|
|
69
|
+
direction: "backspace" | "delete";
|
|
70
|
+
} | {
|
|
71
|
+
kind: "delete-range";
|
|
72
|
+
start: number;
|
|
73
|
+
end: number;
|
|
74
|
+
} | {
|
|
75
|
+
/** Undo the most recent edit action (Ctrl/Cmd+Z). */
|
|
76
|
+
kind: "undo";
|
|
77
|
+
} | {
|
|
78
|
+
/** Redo the most recently undone edit action (Ctrl/Cmd+Y or Ctrl/Cmd+Shift+Z). */
|
|
79
|
+
kind: "redo";
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Built-in status bar component identifiers.
|
|
83
|
+
*
|
|
84
|
+
* - "offset": cursor offset (absolute byte index)
|
|
85
|
+
* - "hex": current byte rendered as hex
|
|
86
|
+
* - "ascii": current byte rendered as ASCII (or nonPrintableChar)
|
|
87
|
+
* - "selection": selection summary text
|
|
88
|
+
* - "slot": renders a named Vue slot in the status bar section
|
|
89
|
+
*/
|
|
90
|
+
export type VueHexStatusBarComponentName = "offset" | "hex" | "ascii" | "selection" | "editable" | "mode" | "column" | "total" | "slot";
|
|
91
|
+
/**
|
|
92
|
+
* Declares a component that can appear in the VueHex status bar.
|
|
93
|
+
*
|
|
94
|
+
* A string is treated as the component name. The object form allows
|
|
95
|
+
* attaching per-component configuration.
|
|
96
|
+
*/
|
|
97
|
+
export type VueHexStatusBarComponent = string | {
|
|
98
|
+
name: string;
|
|
99
|
+
config?: unknown;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Controls where status bar components render.
|
|
103
|
+
*
|
|
104
|
+
* The status bar is split into three sections:
|
|
105
|
+
* - left (left-aligned)
|
|
106
|
+
* - middle (centered)
|
|
107
|
+
* - right (right-aligned)
|
|
108
|
+
*/
|
|
109
|
+
export interface VueHexStatusBarLayout {
|
|
110
|
+
left?: VueHexStatusBarComponent[];
|
|
111
|
+
middle?: VueHexStatusBarComponent[];
|
|
112
|
+
right?: VueHexStatusBarComponent[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Identifies which table column a cell represents when resolving custom classes.
|
|
116
|
+
*/
|
|
117
|
+
export type VueHexCellKind = "hex" | "ascii";
|
|
118
|
+
/**
|
|
119
|
+
* Shape of the payload provided to the cell class resolver, including the absolute index and byte value.
|
|
120
|
+
*/
|
|
121
|
+
export interface VueHexCellClassPayload {
|
|
122
|
+
kind: VueHexCellKind;
|
|
123
|
+
index: number;
|
|
124
|
+
byte: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Allows consumers to return custom class names for individual cells in the hex or ASCII columns.
|
|
128
|
+
*/
|
|
129
|
+
export type VueHexCellClassResolver = (payload: VueHexCellClassPayload) => string | string[] | null | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* Accepts either a single cell class resolver or an ordered list of resolvers.
|
|
132
|
+
*
|
|
133
|
+
* When multiple resolvers are supplied, VueHex will call each resolver and merge
|
|
134
|
+
* the returned class names.
|
|
135
|
+
*/
|
|
136
|
+
export type VueHexCellClassResolverInput = VueHexCellClassResolver | ReadonlyArray<VueHexCellClassResolver>;
|
|
137
|
+
/**
|
|
138
|
+
* Default per-byte cell classifier used by VueHex when `cellClassForByte` is not provided.
|
|
139
|
+
*
|
|
140
|
+
* Returns built-in category classes (`vuehex-category-*`) based on ASCII ranges.
|
|
141
|
+
*/
|
|
142
|
+
export declare const DEFAULT_ASCII_CATEGORY_CELL_CLASS_RESOLVER: VueHexCellClassResolver;
|
|
143
|
+
/**
|
|
144
|
+
* Default printable check for standard ASCII range (0x20-0x7E).
|
|
145
|
+
*/
|
|
146
|
+
export declare const DEFAULT_PRINTABLE_CHECK: VueHexPrintableCheck;
|
|
147
|
+
/**
|
|
148
|
+
* Default renderer that converts printable bytes to their character form.
|
|
149
|
+
*/
|
|
150
|
+
export declare const DEFAULT_ASCII_RENDERER: VueHexAsciiRenderer;
|
|
151
|
+
/**
|
|
152
|
+
* Describes an ASCII preset bundle with labeling and behavior for rendering/printability.
|
|
153
|
+
*/
|
|
154
|
+
export interface VueHexAsciiPreset {
|
|
155
|
+
label: string;
|
|
156
|
+
isPrintable: VueHexPrintableCheck;
|
|
157
|
+
renderAscii: VueHexAsciiRenderer;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Built-in presets for common ASCII rendering scenarios that consumers can reuse.
|
|
161
|
+
*/
|
|
162
|
+
export declare const VUE_HEX_ASCII_PRESETS: Record<"standard" | "latin1" | "visibleWhitespace", VueHexAsciiPreset>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { VueHexCellClassResolver, VueHexCellClassResolverInput } from "./vuehex-api";
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes `cellClassForByte` input into a single resolver function.
|
|
4
|
+
*
|
|
5
|
+
* Rules:
|
|
6
|
+
* - `undefined`: use the default built-in ASCII category resolver.
|
|
7
|
+
* - `null`: disable all per-cell class resolution.
|
|
8
|
+
* - `function`: use as-is.
|
|
9
|
+
* - `array`: filter to functions and merge results (flattened).
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeCellClassForByteResolver(input: VueHexCellClassResolverInput | null | undefined): VueHexCellClassResolver | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const OFFSET_PAD = 8;
|
|
2
|
+
/**
|
|
3
|
+
* Validates that the input data source is a Uint8Array, failing fast otherwise.
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizeSource(source: unknown): Uint8Array;
|
|
6
|
+
/**
|
|
7
|
+
* Sanitizes the requested bytes-per-row value, ensuring downstream layout logic always
|
|
8
|
+
* receives a finite integer at least one byte wide per row.
|
|
9
|
+
*/
|
|
10
|
+
export declare function clampBytesPerRow(value: unknown): number;
|
|
11
|
+
/**
|
|
12
|
+
* Produces the fallback ASCII character used for non-printable bytes, escaping it so
|
|
13
|
+
* it can be dropped directly into HTML markup.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveFallbackChar(value: unknown): string;
|
|
16
|
+
/**
|
|
17
|
+
* Escapes raw text for safe HTML insertion by replacing reserved characters.
|
|
18
|
+
*/
|
|
19
|
+
export declare function escapeHtml(value: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Formats a byte offset as plain hexadecimal text (no HTML) for summary displays.
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatOffsetPlain(value: number, uppercase: boolean): string;
|
|
24
|
+
/**
|
|
25
|
+
* Clamps a numeric value into an inclusive range for shared viewport math helpers.
|
|
26
|
+
*/
|
|
27
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Parses an integer from a DOM element attribute and validates it.
|
|
30
|
+
* Returns null if the attribute doesn't exist or contains an invalid number.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseIndexAttribute(element: HTMLElement, attrName: string): number | null;
|
|
33
|
+
/**
|
|
34
|
+
* Reads a byte index from a hex or ASCII cell element.
|
|
35
|
+
* Returns null if the element is not a valid cell or has no index.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readByteIndexFromElement(element: Element | null): number | null;
|
|
38
|
+
/**
|
|
39
|
+
* Normalizes a theme identifier into a kebab-case token that can be appended to class names.
|
|
40
|
+
*/
|
|
41
|
+
export declare function normalizeThemeKey(value: unknown): string | null;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "./assets/vuehex.css";
|
|
2
|
+
import type { App } from "vue";
|
|
3
|
+
import VueHex from "./components/VueHex.vue";
|
|
4
|
+
export interface VueHexPluginOptions {
|
|
5
|
+
componentName?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function install(app: App, options?: VueHexPluginOptions): void;
|
|
8
|
+
declare const _default: {
|
|
9
|
+
install: typeof install;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
export { VueHex };
|
|
13
|
+
export * from "./components/vuehex-api";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vuehex",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A fast and flexible Vue 3 component for viewing and editing binary data as a hexadecimal view",
|
|
6
6
|
"author": "Vincent Vollers",
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
14
15
|
"import": "./dist/index.js",
|
|
15
|
-
"
|
|
16
|
+
"default": "./dist/index.js"
|
|
16
17
|
},
|
|
17
|
-
"./
|
|
18
|
-
"import": "./dist/styles.js",
|
|
19
|
-
"types": "./dist/styles.d.ts"
|
|
20
|
-
},
|
|
21
|
-
"./style.css": "./dist/style.css",
|
|
18
|
+
"./style.css": "./dist/index.css",
|
|
22
19
|
"./package.json": "./package.json"
|
|
23
20
|
},
|
|
24
21
|
"files": [
|
|
@@ -28,7 +25,7 @@
|
|
|
28
25
|
],
|
|
29
26
|
"scripts": {
|
|
30
27
|
"dev": "vite",
|
|
31
|
-
"build": "vite build && vue-tsc
|
|
28
|
+
"build": "vite build && vue-tsc -p tsconfig.build.json",
|
|
32
29
|
"build:demo": "vite build --mode demo",
|
|
33
30
|
"preview": "vite preview",
|
|
34
31
|
"storybook": "storybook dev -p 6006 --disable-telemetry",
|
package/dist/styles.js
DELETED
|
File without changes
|
|
File without changes
|