PageSourceSearch

https://pupwife.com/scripts/dependencies/cursors/roughjsCursor.js

js pupwife.com collected 2026-09-30 15:05:37 UTC 22,868 bytes, 707 lines download raw bytes

1/**
2 * Rough.js custom cursor system (hand-drawn sketch cursors).
3 */
4(function (global) {
5    'use strict';
6
7    var ROUGH_JS_SRC = 'https://cdn.jsdelivr.net/npm/[email protected]/bundled/rough.js';
8    var ROUGH_LIB_MARK = 'data-pupwife-roughjs-lib';
9
10    var CURSOR_SPECS = {
11        'rough-drawn': {
12            canvasW: 34,
13            canvasH: 34,
14            hotspotX: 12,
15            hotspotY: 12,
16            clickScale: 0.9,
17            draw: drawDrawnPointer
18        },
19        'rough-paw': {
20            canvasW: 32,
21            canvasH: 36,
22            hotspotX: 16,
23            hotspotY: 20,
24            clickScale: 0.88,
25            showClickMarker: true,
26            draw: drawSketchPaw
27        },
28        'rough-pencil': {
29            canvasW: 30,
30            canvasH: 32,
31            hotspotX: 10,
32            hotspotY: 10,
33            clickScale: 0.92,
34            showClickMarker: true,
35            draw: drawPencilNib
36        },
37        'rough-heart': {
38            canvasW: 30,
39            canvasH: 32,
40            hotspotX: 15,
41            hotspotY: 24,
42            clickScale: 0.9,
43            showClickMarker: true,
44            draw: drawHeartPointer
45        }
46    };
47
48    var CURSOR_TYPES = { none: null };
49    Object.keys(CURSOR_SPECS).forEach(function (key) {
50        CURSOR_TYPES[key] = key;
51    });
52
53    var ARROW_POINTS = [
54        [0, 0], [0, 18], [5, 13], [8, 22], [10, 13], [18, 10]
55    ];
56
57    var HAND_POINTS = [
58        [-3, 1], [0, 0], [2, 3], [1, 9], [4, 13], [3, 19],
59        [-1, 21], [-5, 20], [-8, 17], [-10, 13], [-11, 9], [-9, 5], [-6, 3]
60    ];
61
62    var PAW_BEANS_DEFAULT = [
63        [-7, -9], [-2.5, -11], [2.5, -11], [7, -9]
64    ];
65
66    var PAW_BEANS_POINTER = [
67        [-9, -7], [-3, -10], [3, -10], [9, -7]
68    ];
69
70    var HEART_DEFAULT = [
71        [0, 0], [-9, -5], [-10, -12], [-4, -16], [0, -13],
72        [4, -16], [10, -12], [9, -5]
73    ];
74
75    var HEART_POINTER = [
76        [0, 0], [-8, -4], [-9, -11], [-3, -14], [0, -11],
77        [5, -15], [12, -10], [14, -4], [8, -2]
78    ];
79
80    var currentCursor = null;
81    var currentCursorType = 'none';
82    var currentSpec = null;
83    var canvas = null;
84    var roughCanvas = null;
85    var animId = null;
86    var roughLoadPromise = null;
87    var pendingCursorType = null;
88
89    function paintSeed(specId, paintKey) {
90        var s = String(specId) + String(paintKey);
91        var h = 0;
92        for (var i = 0; i < s.length; i++) {
93            h = ((h << 5) - h + s.charCodeAt(i)) | 0;
94        }
95        return Math.abs(h) || 1;
96    }
97
98    function getInkOptions(seed) {
99        var dark = isDarkMode();
100        return {
101            seed: seed,
102            stroke: dark ? '#ece6dc' : '#2e2e2e',
103            fill: dark ? '#3a3644' : '#faf7f2',
104            roughness: 0.95,
105            bowing: 0.85,
106            strokeWidth: 1.25,
107            fillStyle: 'solid',
108            fillWeight: 0.35,
109            disableMultiStroke: true
110        };
111    }
112
113    function getAccentOptions(base) {
114        return {
115            seed: base.seed,
116            stroke: base.stroke,
117            fill: base.stroke,
118            roughness: 1.1,
119            bowing: 1,
120            strokeWidth: 0.85,
121            fillStyle: 'solid',
122            fillWeight: 0.3,
123            disableMultiStroke: true
124        };
125    }
126
127    function drawClickMarker(cursor) {
128        if (!cursor.spec.showClickMarker) return;
129        var ctx = cursor.canvas.getContext('2d');
130        if (!ctx) return;
131
132        var hx = cursor.spec.hotspotX;
133        var hy = cursor.spec.hotspotY;
134        var dark = isDarkMode();
135        var accent = dark ? '#c8a8ff' : '#764ba2';
136
137        ctx.save();
138        ctx.strokeStyle = accent;
139        ctx.fillStyle = accent;
140        ctx.lineWidth = 1.15;
141        ctx.lineCap = 'round';
142        ctx.lineJoin = 'round';
143
144        ctx.beginPath();
145        ctx.moveTo(hx - 4, hy);
146        ctx.lineTo(hx + 4, hy);
147        ctx.moveTo(hx, hy - 4);
148        ctx.lineTo(hx, hy + 4);
149        ctx.stroke();
150
151        ctx.beginPath();
152        ctx.moveTo(hx, hy);
153        ctx.lineTo(hx - 2.5, hy - 4.5);
154        ctx.moveTo(hx, hy);
155        ctx.lineTo(hx + 2.5, hy - 4.5);
156        ctx.stroke();
157
158        ctx.beginPath();
159        ctx.arc(hx, hy, cursor.isClicking ? 1.45 : 1.15, 0, Math.PI * 2);
160        ctx.fill();
161        ctx.restore();
162    }
163
164    function emitDevLog() {
165        if (typeof global.devLog === 'function') {
166            global.devLog.apply(global, arguments);
167        }
168    }
169
170    function emitDevWarn() {
171        if (typeof global.devWarn === 'function') {
172            global.devWarn.apply(global, arguments);
173        }
174    }
175
176    function isDarkMode() {
177        var html = document.documentElement;
178        var body = document.body;
179        return html.classList.contains('dark-mode') ||
180            body.classList.contains('dark-mode') ||
181            html.getAttribute('data-theme') === 'dark';
182    }
183
184    function offsetPoints(ox, oy, points) {
185        return points.map(function (pt) {
186            return [pt[0] + ox, pt[1] + oy];
187        });
188    }
189
190    function isInteractive(el) {
191        if (!el || el === document.documentElement || el === document.body) return false;
192        var tag = el.tagName;
193        if (tag === 'A' || tag === 'BUTTON' || tag === 'SELECT' || tag === 'OPTION') return true;
194        if (tag === 'INPUT') {
195            var type = (el.getAttribute('type') || 'text').toLowerCase();
196            return type === 'button' || type === 'submit' || type === 'reset' || type === 'checkbox' || type === 'radio';
197        }
198        if (el.getAttribute('role') === 'button' || el.getAttribute('role') === 'link') return true;
199        if (el.onclick || el.hasAttribute('onclick')) return true;
200        if (el.classList.contains('clickable') || el.classList.contains('tool-button')) return true;
201        if (el.classList.contains('settings-bubble-select__trigger') || el.classList.contains('settings-bubble-select__option')) return true;
202        return false;
203    }
204
205    function findInteractiveAt(x, y) {
206        var el = document.elementFromPoint(x, y);
207        var depth = 0;
208        while (el && depth < 8) {
209            if (isInteractive(el)) return true;
210            el = el.parentElement;
211            depth++;
212        }
213        return false;
214    }
215
216    function drawDrawnPointer(cursor) {
217        var spec = cursor.spec;
218        var ox = spec.hotspotX;
219        var oy = spec.hotspotY;
220        var opts = cursor.getInkOpts();
221        var points = offsetPoints(ox, oy, cursor.isPointer ? HAND_POINTS : ARROW_POINTS);
222        cursor.rc.polygon(points, opts);
223
224        if (!spec.showClickMarker) {
225            var tipOpts = getAccentOptions(opts);
226            cursor.rc.circle(ox, oy, cursor.isClicking ? 1.6 : 1.2, tipOpts);
227        }
228
229        if (!cursor.isPointer) {
230            var shadeOpts = {
231                seed: opts.seed,
232                roughness: 1.4,
233                bowing: 0.8,
234                stroke: opts.stroke,
235                strokeWidth: 0.6,
236                disableMultiStroke: true,
237                disableMultiStrokeFill: true
238            };
239            cursor.rc.line(ox + 2, oy + 14, ox + 6, oy + 11, shadeOpts);
240            cursor.rc.line(ox + 2, oy + 16, ox + 5, oy + 13, shadeOpts);
241        }
242
243        drawClickMarker(cursor);
244    }
245
246    function drawSketchPaw(cursor) {
247        var spec = cursor.spec;
248        var ox = spec.hotspotX;
249        var oy = spec.hotspotY;
250        var opts = cursor.getInkOpts();
251        var beans = cursor.isPointer ? PAW_BEANS_POINTER : PAW_BEANS_DEFAULT;
252        var squish = cursor.isClicking ? 1.15 : 1;
253
254        cursor.rc.circle(ox, oy, 8 * squish, opts);
255
256        beans.forEach(function (bean) {
257            cursor.rc.circle(ox + bean[0], oy + bean[1], cursor.isClicking ? 3.8 : 3.4, opts);
258        });
259
260        drawClickMarker(cursor);
261    }
262
263    function drawPencilNib(cursor) {
264        var spec = cursor.spec;
265        var ox = spec.hotspotX;
266        var oy = spec.hotspotY;
267        var opts = cursor.getInkOpts();
268
269        cursor.rc.polygon(offsetPoints(ox, oy, [
270            [0, 0], [0, 14], [5, 10]
271        ]), opts);
272
273        cursor.rc.line(ox + 5, oy + 10, ox + 17, oy + 22, {
274            seed: opts.seed,
275            roughness: 1.2,
276            bowing: 1,
277            stroke: opts.stroke,
278            strokeWidth: 1.4,
279            disableMultiStroke: true
280        });
281
282        cursor.rc.rectangle(ox + 12, oy + 18, 9, 7, {
283            seed: opts.seed,
284            roughness: 1.3,
285            bowing: 0.9,
286            stroke: opts.stroke,
287            strokeWidth: 1,
288            fill: opts.fill,
289            fillStyle: 'solid',
290            fillWeight: 0.35,
291            disableMultiStroke: true
292        });
293
294        var shadeOpts = {
295            seed: opts.seed,
296            roughness: 1.3,
297            bowing: 0.7,
298            stroke: opts.stroke,
299            strokeWidth: 0.55,
300            disableMultiStroke: true,
301            disableMultiStrokeFill: true
302        };
303        cursor.rc.line(ox + 7, oy + 13, ox + 14, oy + 20, shadeOpts);
304        cursor.rc.line(ox + 9, oy + 15, ox + 15, oy + 21, shadeOpts);
305
306        if (cursor.smudgeLevel > 0) {
307            var smudgeOpts = getAccentOptions(opts);
308            for (var i = 0; i < cursor.smudgeLevel; i++) {
309                cursor.rc.circle(
310                    ox - 2 - i * 2,
311                    oy + 4 + i * 2,
312                    0.8 + i * 0.2,
313                    smudgeOpts
314                );
315            }
316        }
317
318        drawClickMarker(cursor);
319    }
320
321    function drawHeartPointer(cursor) {
322        var spec = cursor.spec;
323        var ox = spec.hotspotX;
324        var oy = spec.hotspotY;
325        var opts = cursor.getInkOpts();
326        var dark = isDarkMode();
327        var heartOpts = Object.assign({}, opts, {
328            stroke: cursor.isPointer ? '#c8a8ff' : opts.stroke,
329            fill: cursor.isPointer ? (dark ? '#4a3d5c' : '#f3ebff') : opts.fill
330        });
331
332        var points = offsetPoints(ox, oy, cursor.isPointer ? HEART_POINTER : HEART_DEFAULT);
333        cursor.rc.polygon(points, heartOpts);
334
335        drawClickMarker(cursor);
336    }
337
338    function RoughSketchCursor(spec, targetCanvas, rc, cursorTypeKey) {
339        this.cursorTypeKey = cursorTypeKey || 'rough-sketch';
340        this.spec = spec;
341        this.canvas = targetCanvas;
342        this.rc = rc;
343        this.mouseX = typeof global.__pupwifeLastMouseX === 'number' ? global.__pupwifeLastMouseX : global.innerWidth / 2;
344        this.mouseY = typeof global.__pupwifeLastMouseY === 'number' ? global.__pupwifeLastMouseY : global.innerHe
344ight / 2;
345        this.speed = 0;
346        this.smudgeLevel = 0;
347        this.isPointer = false;
348        this.isClicking = false;
349        this.dirty = true;
350        this.lastPaintKey = '';
351        this.paintSeed = 1;
352        this._onMove = this.handleMouseMove.bind(this);
353        this._onDown = this.handleMouseDown.bind(this);
354        this._onUp = this.handleMouseUp.bind(this);
355        this._onSetting = this.handleSettingChange.bind(this);
356
357        document.addEventListener('mousemove', this._onMove, { passive: true });
358        document.addEventListener('mousedown', this._onDown, { passive: true });
359        document.addEventListener('mouseup', this._onUp, { passive: true });
360        document.addEventListener('pupwife:setting-changed', this._onSetting);
361
362        this.isPointer = findInteractiveAt(this.mouseX, this.mouseY);
363        this.applyTransform();
364        this.draw(true);
365    }
366
367    RoughSketchCursor.prototype.getSmudgeLevel = function () {
368        if (this.cursorTypeKey !== 'rough-pencil') return 0;
369        if (this.speed < 5) return 0;
370        if (this.speed < 10) return 1;
371        if (this.speed < 18) return 2;
372        return 3;
373    };
374
375    RoughSketchCursor.prototype.getPaintKey = function () {
376        return [
377            this.isPointer ? '1' : '0',
378            this.isClicking ? '1' : '0',
379            isDarkMode() ? '1' : '0',
380            this.getSmudgeLevel()
381        ].join('|');
382    };
383
384    RoughSketchCursor.prototype.getInkOpts = function () {
385        return getInkOptions(this.paintSeed);
386    };
387
388    RoughSketchCursor.prototype.syncPaintState = function (force) {
389        var key = this.getPaintKey();
390        var keyChanged = key !== this.lastPaintKey;
391
392        if (!force && !this.dirty && !keyChanged) {
393            return false;
394        }
395
396        this.lastPaintKey = key;
397        this.smudgeLevel = this.getSmudgeLevel();
398        this.paintSeed = paintSeed(this.cursorTypeKey, key);
399        this.dirty = false;
400
401        var roughApi = getRoughApi();
402        if (roughApi) {
403            this.rc = roughApi.canvas(this.canvas, { seed: this.paintSeed });
404        }
405
406        return true;
407    };
408
409    RoughSketchCursor.prototype.markDirty = function () {
410        this.dirty = true;
411    };
412
413    RoughSketchCursor.prototype.handleMouseMove = function (e) {
414        var dx = e.clientX - this.mouseX;
415        var dy = e.clientY - this.mouseY;
416        this.speed = Math.sqrt(dx * dx + dy * dy);
417        global.__pupwifeLastMouseX = e.clientX;
418        global.__pupwifeLastMouseY = e.clientY;
419        this.mouseX = e.clientX;
420        this.mouseY = e.clientY;
421
422        var nextPointer = findInteractiveAt(e.clientX, e.clientY);
423        if (nextPointer !== this.isPointer) {
424            this.isPointer = nextPointer;
425            this.markDirty();
426        } else if (this.cursorTypeKey === 'rough-pencil') {
427            var nextSmudge = this.getSmudgeLevel();
428            if (nextSmudge !== this.smudgeLevel) {
429                this.markDirty();
430            }
431        }
432    };
433
434    RoughSketchCursor.prototype.handleMouseDown = function () {
435        if (!this.isClicking) {
436            this.isClicking = true;
437            this.markDirty();
438        }
439    };
440
441    RoughSketchCursor.prototype.handleMouseUp = function () {
442        if (this.isClicking) {
443            this.isClicking = false;
444            this.markDirty();
445        }
446    };
447
448    RoughSketchCursor.prototype.handleSettingChange = function () {
449        this.markDirty();
450        this.draw(true);
451    };
452
453    RoughSketchCursor.prototype.applyTransform = function () {
454        var scale = this.isClicking ? (this.spec.clickScale || 0.9) : 1;
455        var left = this.mouseX - this.spec.hotspotX;
456        var top = this.mouseY - this.spec.hotspotY;
457        this.canvas.style.transform =
458            'translate3d(' + left + 'px,' + top + 'px,0) scale(' + scale + ')';
459    };
460
461    RoughSketchCursor.prototype.draw = function (force) {
462        if (!this.rc || !this.spec.draw) return;
463        if (!this.syncPaintState(!!force)) return;
464
465        var ctx = this.canvas.getContext('2d');
466        if (!ctx) return;
467
468        ctx.clearRect(0, 0, this.spec.canvasW, this.spec.canvasH);
469        this.spec.draw(this);
470    };
471
472    RoughSketchCursor.prototype.update = function () {
473        this.applyTransform();
474        this.draw(false);
475        this.speed *= 0.78;
476    };
477
478    RoughSketchCursor.prototype.destroy = function () {
479        document.removeEventListener('mousemove', this._onMove);
480        document.removeEventListener('mousedown', this._onDown);
481        document.removeEventListener('mouseup', this._onUp);
482        document.removeEventListener('pupwife:setting-changed', this._onSetting);
483    };
484
485    function getRoughApi() {
486        if (global.rough && typeof global.rough.canvas === 'function') return global.rough;
487        return null;
488    }
489
490    function findRoughLibScript() {
491        return document.querySelector('script[' + ROUGH_LIB_MARK + '], script[src*="bundled/rough.js"]');
492    }
493
494    function ensureRoughJs() {
495        var existing = getRoughApi();
496        if (existing) return Promise.resolve(existing);
497
498        if (roughLoadPromise) {
499            return roughLoadPromise.then(function (api) {
500                if (api) return api;
501                roughLoadPromise = null;
502                return ensureRoughJs();
503            });
504        }
505
506        roughLoadPromise = new Promise(function (resolve) {
507            var tag = findRoughLibScript();
508            if (tag) {
509                if (getRoughApi()) {
510                    resolve(getRoughApi());
511                    return;
512                }
513                var done = function () { resolve(getRoughApi()); };
514                tag.addEventListener('load', done, { once: true });
515                tag.addEventListener('error', done, { once: true });
516                global.setTimeout(function () { resolve(getRoughApi()); }, 100);
517                return;
518            }
519
520            var script = document.createElement('script');
521            script.src = ROUGH_JS_SRC;
522            script.setAttribute(ROUGH_LIB_MARK, '1');
523            script.async = false;
524            script.onload = function () { resolve(getRoughApi()); };
525            script.onerror = function () {
526                emitDevWarn('Rough.js failed to load — sketch cursors unavailable.');
527                resolve(null);
528            };
529            document.head.appendChild(script);
530        });
531
532        return roughLoadPromise;
533    }
534
535    function ensureStyles() {
536        if (document.getElementById('roughjs-cursor-style')) return;
537        var style = document.createElement('style');
538        style.id = 'roughjs-cursor-style';
539        style.textContent =
540            'html.roughjs-cursor-active, html.roughjs-cursor-active *, ' +
541            'body.roughjs-cursor-active, body.roughjs-cursor-active * { cursor: none !important; }' +
542            '#roughjs-sketch-cursor { pointer-events: none !important; touch-action: none !important; }';
543        document.head.appendChild(style);
544    }
545
546    function trackLastMouse(e) {
547        global.__pupwifeLastMouseX = e.clientX;
548        global.__pupwifeLastMouseY = e.clientY;
549    }
550
551    function ensureCanvas(spec) {
552        if (!canvas) {
553            canvas = document.createElement('canvas');
554            canvas.id = 'roughjs-sketch-cursor';
555            canvas.setAttribute('aria-hidden', 'true');
556            canvas.style.cssText =
557                'position:fixed;top:0;left:0;pointer-events:none!important;will-change:transform;z-index:2147483000;';
558            document.body.appendChild(canvas);
559        }
560
561        canvas.width = spec.canvasW;
562        canvas.height = spec.canvasH;
563        canvas.style.width = spec.canvasW + 'px';
564        canvas.style.height = spec.canvasH + 'px';
565        canvas.style.transformOrigin = spec.hotspotX + 'px ' + spec.hotspotY + 'px';
566
567        return canvas;
568    }
569
570    function setCursorActive(active) {
571        document.documentElement.classList.toggle('roughjs-cursor-active', active);
572        document.body.classList.toggle('roughjs-cursor-active', active);
573    }
574
575    function stopLoop() {
576        if (animId) {
577            cancelAnimationFrame(animId);
578            animId = null;
579        }
580    }
581
582    function startLoop() {
583        stopLoop();
584        function tick() {
585            if (currentCursor && typeof currentCursor.update === 'function') {
586                currentCursor.update();
587            }
588            animId = requestAnimationFrame(tick);
589        }
590        animId = requestAnimationFrame(tick);
591    }
592
593    function teardownCursor() {
594        stopLoop();
595        if (currentCursor && typeof currentCursor.destroy === 'function') {
596            currentCursor.destroy();
597        }
598        currentCursor = null;
599        currentSpec = null;
600        roughCanvas = null;
601        if (canvas) {
602            var ctx = canvas.getContext('2d');
603            if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);
604            if (canvas.parentNode) canvas.parentNode.removeChild(canvas);
605            canvas = null;
606        }
607        setCursorActive(false);
608    }
609
610    function activateRoughCursor(cursorType) {
611        var spec = CURSOR_SPECS[cursorType];
612        var roughApi = getRoughApi();
613        if (!spec || !roughApi) {
614            emitDevWarn('Rough.js cursor unavailable: ' + cursorType);
615            return false;
616        }
617
618        ensureStyles();
619        document.addEventListener('mousemove', trackLastMouse, { passive: true });
620
621        var targetCanvas = ensureCanvas(spec);
622        roughCanvas = roughApi.canvas(targetCanvas);
623        currentSpec = spec;
624        currentCursor = new RoughSketchCursor(spec, targetCanvas, roughCanvas, cursorType);
625        setCursorActive(true);
626        startLoop();
627        emitDevLog('Rough.js cursor set to: ' + cursorType);
628        return true;
629    }
630
631    function setCursor(cursorType) {
632        var nextType = cursorType || 'none';
633
634        if (nextType === currentCursorType && currentCursor && getRoughApi()) {
635            return Promise.resolve();
636        }
637
638        pendingCursorType = nextType;
639        teardownCursor();
640        currentCursorType = pendingCursorType;
641
642        if (!cursorType || cursorType === 'none') {
643            pendingCursorType = null;
644            return Promise.resolve();
645        }
646
647        if (!CURSOR_SPECS[cursorType]) {
648            emitDevWarn('Unknown Rough.js cursor type: ' + cursorType);
649            pendingCursorType = null;
650            return Promise.resolve();
651        }
652
653        return ensureRoughJs()
654            .then(function (roughApi) {
655                if (!roughApi) {
656                    console.warn('[Sketch cursor] Rough.js did not load. Check network or ad blockers.');
657                    pendingCursorType = null;
658                    return;
659                }
660                if (pendingCursorType !== cursorType) return;
661                if (!activateRoughCursor(cursorType)) {
662                    console.warn('[Sketch cursor] Failed to start: ' + cursorType);
663                    pendingCursorType = null;
664                }
665            })
666            .catch(function (err) {
667                pendingCursorType = null;
668                console.error('Failed to activate Rough.js cursor:', err);
669            });
670    }
671
672    function getCursor() {
673        return currentCursorType;
674    }
675
676    function cleanup() {
677        teardownCursor();
678        document.removeEventListener('mousemove', trackLastMouse);
679        var style = document.getElementById('roughjs-cursor-style');
680        if (style) style.remove();
681    }
682
683    function readSavedRoughCursor() {
684        try {
685            var raw = global.localStorage && global.localStorage.getItem('puppywifey_settings');
686            if (!raw) return null;
687            var parsed = JSON.parse(raw);
688            if (parsed.customCursor && String(parsed.customCursor).indexOf('rough-') === 0) {
689                return parsed.customCursor;
690            }
691        } catch (e) { /* ignore */ }
692        return null;
693    }
694
695    global.RoughJSCursor = {
696        setCursor: setCursor,
697        getCursor: getCursor,
698        cleanup: cleanup,
699        CURSOR_TYPES: CURSOR_TYPES,
700        SPECS: CURSOR_SPECS
701    };
702
703    document.addEventListener('pupwife:apply-custom-cursor', function () {
704        var saved = readSavedRoughCursor();
705        if (saved) setCursor(saved);
706    });
707})(window);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.