UNPKG

@textea/json-viewer

Version:

Interactive Json Viewer, but not only a json viewer

1,312 lines (1,291 loc) 75.3 kB
/// <reference types="./index.d.ts" /> import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { Box, InputBase, NoSsr, SvgIcon, createTheme, ThemeProvider, Paper } from '@mui/material'; import { createContext, useContext, useState, useRef, useCallback, useMemo, useEffect, memo } from 'react'; import { create, useStore, createStore } from 'zustand'; import copyToClipboard from 'copy-to-clipboard'; function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n} const lightColorspace = { scheme: 'Light Theme', author: 'mac gainor (https://github.com/mac-s-g)', base00: 'rgba(0, 0, 0, 0)', base01: 'rgb(245, 245, 245)', base02: 'rgb(235, 235, 235)', base03: '#93a1a1', base04: 'rgba(0, 0, 0, 0.3)', base05: '#586e75', base06: '#073642', base07: '#002b36', base08: '#d33682', base09: '#cb4b16', base0A: '#ffd500', base0B: '#859900', base0C: '#6c71c4', base0D: '#586e75', base0E: '#2aa198', base0F: '#268bd2' }; const darkColorspace = { scheme: 'Dark Theme', author: 'Chris Kempson (http://chriskempson.com)', base00: '#181818', base01: '#282828', base02: '#383838', base03: '#585858', base04: '#b8b8b8', base05: '#d8d8d8', base06: '#e8e8e8', base07: '#f8f8f8', base08: '#ab4642', base09: '#dc9656', base0A: '#f7ca88', base0B: '#a1b56c', base0C: '#86c1b9', base0D: '#7cafc2', base0E: '#ba8baf', base0F: '#a16946' }; const DefaultKeyRenderer = ()=>null; DefaultKeyRenderer.when = ()=>false; const createJsonViewerStore = (props)=>{ return create()((set, get)=>{ var _props_rootName, _props_indentWidth, _props_keyRenderer, _props_enableAdd, _props_enableDelete, _props_enableClipboard, _props_editable, _props_onChange, _props_onCopy, _props_onSelect, _props_onAdd, _props_onDelete, _props_defaultInspectDepth, _props_defaultInspectControl, _props_maxDisplayLength, _props_groupArraysAfterLength, _props_collapseStringsAfterLength, _props_objectSortKeys, _props_quotesOnKeys, _props_displayDataTypes, _props_displaySize, _props_displayComma, _props_highlightUpdates; return { // provided by user rootName: (_props_rootName = props.rootName) !== null && _props_rootName !== void 0 ? _props_rootName : 'root', indentWidth: (_props_indentWidth = props.indentWidth) !== null && _props_indentWidth !== void 0 ? _props_indentWidth : 3, keyRenderer: (_props_keyRenderer = props.keyRenderer) !== null && _props_keyRenderer !== void 0 ? _props_keyRenderer : DefaultKeyRenderer, enableAdd: (_props_enableAdd = props.enableAdd) !== null && _props_enableAdd !== void 0 ? _props_enableAdd : false, enableDelete: (_props_enableDelete = props.enableDelete) !== null && _props_enableDelete !== void 0 ? _props_enableDelete : false, enableClipboard: (_props_enableClipboard = props.enableClipboard) !== null && _props_enableClipboard !== void 0 ? _props_enableClipboard : true, editable: (_props_editable = props.editable) !== null && _props_editable !== void 0 ? _props_editable : false, onChange: (_props_onChange = props.onChange) !== null && _props_onChange !== void 0 ? _props_onChange : ()=>{}, onCopy: (_props_onCopy = props.onCopy) !== null && _props_onCopy !== void 0 ? _props_onCopy : undefined, onSelect: (_props_onSelect = props.onSelect) !== null && _props_onSelect !== void 0 ? _props_onSelect : undefined, onAdd: (_props_onAdd = props.onAdd) !== null && _props_onAdd !== void 0 ? _props_onAdd : undefined, onDelete: (_props_onDelete = props.onDelete) !== null && _props_onDelete !== void 0 ? _props_onDelete : undefined, defaultInspectDepth: (_props_defaultInspectDepth = props.defaultInspectDepth) !== null && _props_defaultInspectDepth !== void 0 ? _props_defaultInspectDepth : 5, defaultInspectControl: (_props_defaultInspectControl = props.defaultInspectControl) !== null && _props_defaultInspectControl !== void 0 ? _props_defaultInspectControl : undefined, maxDisplayLength: (_props_maxDisplayLength = props.maxDisplayLength) !== null && _props_maxDisplayLength !== void 0 ? _props_maxDisplayLength : 30, groupArraysAfterLength: (_props_groupArraysAfterLength = props.groupArraysAfterLength) !== null && _props_groupArraysAfterLength !== void 0 ? _props_groupArraysAfterLength : 100, collapseStringsAfterLength: props.collapseStringsAfterLength === false ? Number.MAX_VALUE : (_props_collapseStringsAfterLength = props.collapseStringsAfterLength) !== null && _props_collapseStringsAfterLength !== void 0 ? _props_collapseStringsAfterLength : 50, objectSortKeys: (_props_objectSortKeys = props.objectSortKeys) !== null && _props_objectSortKeys !== void 0 ? _props_objectSortKeys : false, quotesOnKeys: (_props_quotesOnKeys = props.quotesOnKeys) !== null && _props_quotesOnKeys !== void 0 ? _props_quotesOnKeys : true, displayDataTypes: (_props_displayDataTypes = props.displayDataTypes) !== null && _props_displayDataTypes !== void 0 ? _props_displayDataTypes : true, displaySize: (_props_displaySize = props.displaySize) !== null && _props_displaySize !== void 0 ? _props_displaySize : true, displayComma: (_props_displayComma = props.displayComma) !== null && _props_displayComma !== void 0 ? _props_displayComma : false, highlightUpdates: (_props_highlightUpdates = props.highlightUpdates) !== null && _props_highlightUpdates !== void 0 ? _props_highlightUpdates : false, // internal state inspectCache: {}, hoverPath: null, colorspace: lightColorspace, value: props.value, prevValue: undefined, getInspectCache: (path, nestedIndex)=>{ const target = nestedIndex !== undefined ? path.join('.') + "[".concat(nestedIndex, "]nt") : path.join('.'); return get().inspectCache[target]; }, setInspectCache: (path, action, nestedIndex)=>{ const target = nestedIndex !== undefined ? path.join('.') + "[".concat(nestedIndex, "]nt") : path.join('.'); set((state)=>({ inspectCache: { ...state.inspectCache, [target]: typeof action === 'function' ? action(state.inspectCache[target]) : action } })); }, setHover: (path, nestedIndex)=>{ set({ hoverPath: path ? { path, nestedIndex } : null }); } }; }); }; // @ts-expect-error we intentionally want to pass undefined to the context // See https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24509#issuecomment-382213106 const JsonViewerStoreContext = createContext(undefined); JsonViewerStoreContext.Provider; const useJsonViewerStore = (selector, equalityFn)=>{ const store = useContext(JsonViewerStoreContext); return useStore(store, selector, equalityFn); }; const useTextColor = ()=>{ return useJsonViewerStore((store)=>store.colorspace.base07); }; // reference: https://github.com/immerjs/immer/blob/main/src/utils/common.ts const objectCtorString = Object.prototype.constructor.toString(); function isPlainObject(value) { if (!value || typeof value !== 'object') return false; const proto = Object.getPrototypeOf(value); if (proto === null) return true; const Ctor = Object.hasOwnProperty.call(proto, 'constructor') && proto.constructor; if (Ctor === Object) return true; return typeof Ctor === 'function' && Function.toString.call(Ctor) === objectCtorString; } function shouldShallowCopy(value) { if (!value) return false; return isPlainObject(value) || Array.isArray(value) || value instanceof Map || value instanceof Set; } function shallowCopy(value) { if (Array.isArray(value)) return Array.prototype.slice.call(value); if (value instanceof Set) return new Set(value); if (value instanceof Map) return new Map(value); if (typeof value === 'object' && value !== null) { return Object.assign({}, value); } return value; } function _applyValue(input, path, value) { let visitedMapping = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : new Map(); if (typeof input !== 'object' || input === null) { if (path.length !== 0) { throw new Error('path is incorrect'); } return value; } const shouldCopy = shouldShallowCopy(input); if (shouldCopy) { let copiedInput = visitedMapping.get(input); if (!copiedInput) { copiedInput = shallowCopy(input); visitedMapping.set(input, copiedInput); } input = copiedInput; } const [key, ...restPath] = path; if (key !== undefined) { if (key === '__proto__') { throw new TypeError('Modification of prototype is not allowed'); } if (restPath.length > 0) { input[key] = _applyValue(input[key], restPath, value, visitedMapping); } else { input[key] = value; } } return input; } /** * Apply a value to a given path of an object. */ function applyValue(input, path, value) { return _applyValue(input, path, value); } /** * Delete a value from a given path of an object. */ function deleteValue(input, path, value) { if (typeof input !== 'object' || input === null) { if (path.length !== 0) { throw new Error('path is incorrect'); } return value; } const shouldCopy = shouldShallowCopy(input); if (shouldCopy) input = shallowCopy(input); const [key, ...restPath] = path; if (key !== undefined) { if (key === '__proto__') { throw new TypeError('Modification of prototype is not allowed'); } if (restPath.length > 0) { input[key] = deleteValue(input[key], restPath, value); } else { if (Array.isArray(input)) { input.splice(Number(key), 1); } else { delete input[key]; } } } return input; } /** * Define custom data types for any data structure */ function defineDataType(param) { let { is, serialize, deserialize, Component, Editor, PreComponent, PostComponent } = param; return { is, serialize, deserialize, Component, Editor, PreComponent, PostComponent }; } const isCycleReference = (root, path, value)=>{ if (root === null || value === null) { return false; } if (typeof root !== 'object') { return false; } if (typeof value !== 'object') { return false; } if (Object.is(root, value) && path.length !== 0) { return ''; } const currentPath = []; const arr = [ ...path ]; let currentRoot = root; while(currentRoot !== value || arr.length !== 0){ if (typeof currentRoot !== 'object' || currentRoot === null) { return false; } if (Object.is(currentRoot, value)) { return currentPath.reduce((path, value, currentIndex)=>{ if (typeof value === 'number') { return path + "[".concat(value, "]"); } return path + "".concat(currentIndex === 0 ? '' : '.').concat(value); }, ''); } const target = arr.shift(); currentPath.push(target); currentRoot = currentRoot[target]; } return false; }; function getValueSize(value) { if (value === null || undefined) { return 0; } else if (Array.isArray(value)) { return value.length; } else if (value instanceof Map || value instanceof Set) { return value.size; } else if (value instanceof Date) { return 1; } else if (typeof value === 'object') { return Object.keys(value).length; } else if (typeof value === 'string') { return value.length; } return 1; } function segmentArray(arr, size) { const result = []; let index = 0; while(index < arr.length){ result.push(arr.slice(index, index + size)); index += size; } return result; } /** * A safe version of `JSON.stringify` that handles circular references and BigInts. * * *This function might be changed in the future to support more types. Use it with caution.* * * @param obj A JavaScript value, usually an object or array, to be converted. * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. * @returns */ function safeStringify(obj, space) { const seenValues = []; function replacer(key, value) { // https://github.com/GoogleChromeLabs/jsbi/issues/30 if (typeof value === 'bigint') return value.toString(); // Map and Set are not supported by JSON.stringify if (value instanceof Map) { if ('toJSON' in value && typeof value.toJSON === 'function') return value.toJSON(); if (value.size === 0) return {}; if (seenValues.includes(value)) return '[Circular]'; seenValues.push(value); const entries = Array.from(value.entries()); if (entries.every((param)=>{ let [key] = param; return typeof key === 'string' || typeof key === 'number'; })) { return Object.fromEntries(entries); } // if keys are not string or number, we can't convert to object // fallback to default behavior return {}; } if (value instanceof Set) { if ('toJSON' in value && typeof value.toJSON === 'function') return value.toJSON(); if (seenValues.includes(value)) return '[Circular]'; seenValues.push(value); return Array.from(value.values()); } // https://stackoverflow.com/a/72457899 if (typeof value === 'object' && value !== null && Object.keys(value).length) { const stackSize = seenValues.length; if (stackSize) { // clean up expired references for(let n = stackSize - 1; n >= 0 && seenValues[n][key] !== value; --n){ seenValues.pop(); } if (seenValues.includes(value)) return '[Circular]'; } seenValues.push(value); } return value; } return JSON.stringify(obj, replacer, space); } async function copyString(value) { if ('clipboard' in navigator) { try { await navigator.clipboard.writeText(value); } catch { // When navigator.clipboard throws an error, fallback to copy-to-clipboard package } } // fallback to copy-to-clipboard when navigator.clipboard is not available copyToClipboard(value); } function pathValueDefaultGetter(value, key) { if (value === null || value === undefined) { return null; } if (value instanceof Map || value instanceof WeakMap) { return value.get(key); } if (value instanceof Set) { return Array.from(value)[key]; } if (value instanceof WeakSet) { throw new Error('WeakSet is not supported'); } if (Array.isArray(value)) { return value[Number(key)]; } if (typeof value === 'object') { return value[key]; } return null; } /** * Get the value at a given path in an object. * Passing custom getters allows you to handle custom data structures. * @experimental This function is not yet stable and may change in the future. */ function getPathValue(obj, path) { let customGetters = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : []; try { // @ts-ignore return path.reduce((acc, key, index)=>{ if (acc === null || acc === undefined) { console.error('Invalid path or value encountered at path', path.slice(0, index)); throw new Error('Invalid path or value encountered'); } for (const handler of customGetters){ const currentPath = path.slice(0, index + 1); if (handler.is(acc, currentPath)) { return handler.handler(acc, key); } } return pathValueDefaultGetter(acc, key); }, obj); } catch (error) { console.error(error); return null // or throw error? ; } } /** * useClipboard hook accepts one argument options in which copied status timeout duration is defined (defaults to 2000). Hook returns object with properties: * - copy – function to copy value to clipboard * - copied – value that indicates that copy handler was called less than options.timeout ms ago * - reset – function to clear timeout and reset copied to false */ function useClipboard() { let { timeout = 2000 } = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; const [copied, setCopied] = useState(false); const copyTimeout = useRef(null); const handleCopyResult = useCallback((value)=>{ const current = copyTimeout.current; if (current) { window.clearTimeout(current); } copyTimeout.current = window.setTimeout(()=>setCopied(false), timeout); setCopied(value); }, [ timeout ]); const onCopy = useJsonViewerStore((store)=>store.onCopy); const copy = useCallback(async (path, value)=>{ if (typeof onCopy === 'function') { try { await onCopy(path, value, copyString); handleCopyResult(true); } catch (error) { console.error("error when copy ".concat(path.length === 0 ? 'src' : "src[".concat(path.join('.')), "]"), error); } } else { try { const valueToCopy = safeStringify(typeof value === 'function' ? value.toString() : value, ' '); await copyString(valueToCopy); handleCopyResult(true); } catch (error) { console.error("error when copy ".concat(path.length === 0 ? 'src' : "src[".concat(path.join('.')), "]"), error); } } }, [ handleCopyResult, onCopy ]); const reset = useCallback(()=>{ setCopied(false); if (copyTimeout.current) { clearTimeout(copyTimeout.current); } }, []); return { copy, reset, copied }; } function useIsCycleReference(path, value) { const rootValue = useJsonViewerStore((store)=>store.value); return useMemo(()=>isCycleReference(rootValue, path, value), [ path, value, rootValue ]); } function useInspect(path, value, nestedIndex) { const depth = path.length; const isTrap = useIsCycleReference(path, value); const getInspectCache = useJsonViewerStore((store)=>store.getInspectCache); const setInspectCache = useJsonViewerStore((store)=>store.setInspectCache); const defaultInspectDepth = useJsonViewerStore((store)=>store.defaultInspectDepth); const defaultInspectControl = useJsonViewerStore((store)=>store.defaultInspectControl); useEffect(()=>{ const inspect = getInspectCache(path, nestedIndex); if (inspect !== undefined) { return; } // item with nestedIndex should not be inspected if (nestedIndex !== undefined) { setInspectCache(path, false, nestedIndex); return; } // do not inspect when it is a cycle reference, otherwise there will have a loop const shouldInspect = isTrap ? false : typeof defaultInspectControl === 'function' ? defaultInspectControl(path, value) : depth < defaultInspectDepth; setInspectCache(path, shouldInspect); }, [ defaultInspectDepth, defaultInspectControl, depth, getInspectCache, isTrap, nestedIndex, path, value, setInspectCache ]); const [inspect, set] = useState(()=>{ const shouldInspect = getInspectCache(path, nestedIndex); if (shouldInspect !== undefined) { return shouldInspect; } if (nestedIndex !== undefined) { return false; } return isTrap ? false : typeof defaultInspectControl === 'function' ? defaultInspectControl(path, value) : depth < defaultInspectDepth; }); const setInspect = useCallback((apply)=>{ set((oldState)=>{ const newState = typeof apply === 'boolean' ? apply : apply(oldState); setInspectCache(path, newState, nestedIndex); return newState; }); }, [ nestedIndex, path, setInspectCache ]); return [ inspect, setInspect ]; } const DataBox = (props)=>/*#__PURE__*/ jsx(Box, { component: "div", ...props, sx: { display: 'inline-block', ...props.sx } }); const DataTypeLabel = (param)=>{ let { dataType, enable = true } = param; if (!enable) return null; return /*#__PURE__*/ jsx(DataBox, { className: "data-type-label", sx: { mx: 0.5, fontSize: '0.7rem', opacity: 0.8, userSelect: 'none' }, children: dataType }); }; /** * Enhanced version of `defineDataType` that creates a `DataType` with editor and a optional type label. * It will take care of the color and all the necessary props. * * *All of the built-in data types are defined with this function.* * * @param config.type The type name. * @param config.colorKey The color key in the colorspace. ('base00' - 'base0F') * @param config.displayTypeLabel Whether to display the type label. * @param config.Renderer The component to render the value. */ function defineEasyType(param) { let { is, serialize, deserialize, type, colorKey, displayTypeLabel = true, Renderer } = param; const Render = /*#__PURE__*/ memo(Renderer); const EasyType = (props)=>{ const storeDisplayDataTypes = useJsonViewerStore((store)=>store.displayDataTypes); const color = useJsonViewerStore((store)=>store.colorspace[colorKey]); const onSelect = useJsonViewerStore((store)=>store.onSelect); return /*#__PURE__*/ jsxs(DataBox, { onClick: ()=>onSelect === null || onSelect === void 0 ? void 0 : onSelect(props.path, props.value), sx: { color }, children: [ displayTypeLabel && storeDisplayDataTypes && /*#__PURE__*/ jsx(DataTypeLabel, { dataType: type }), /*#__PURE__*/ jsx(DataBox, { className: "".concat(type, "-value"), children: /*#__PURE__*/ jsx(Render, { path: props.path, inspect: props.inspect, setInspect: props.setInspect, value: props.value, prevValue: props.prevValue }) }) ] }); }; EasyType.displayName = "easy-".concat(type, "-type"); if (!serialize || !deserialize) { return { is, Component: EasyType }; } const EasyTypeEditor = (param)=>{ let { value, setValue, abortEditing, commitEditing } = param; const color = useJsonViewerStore((store)=>store.colorspace[colorKey]); const handleKeyDown = useCallback((event)=>{ if (event.key === 'Enter') { event.preventDefault(); commitEditing(value); } if (event.key === 'Escape') { event.preventDefault(); abortEditing(); } }, [ abortEditing, commitEditing, value ]); const handleChange = useCallback((event)=>{ setValue(event.target.value); }, [ setValue ]); return /*#__PURE__*/ jsx(InputBase, { autoFocus: true, value: value, onChange: handleChange, onKeyDown: handleKeyDown, size: "small", multiline: true, sx: { color, padding: 0.5, borderStyle: 'solid', borderColor: 'black', borderWidth: 1, fontSize: '0.8rem', fontFamily: 'monospace', display: 'inline-flex' } }); }; EasyTypeEditor.displayName = "easy-".concat(type, "-type-editor"); return { is, serialize, deserialize, Component: EasyType, Editor: EasyTypeEditor }; } const booleanType = defineEasyType({ is: (value)=>typeof value === 'boolean', type: 'bool', colorKey: 'base0E', serialize: (value)=>value.toString(), deserialize: (value)=>{ if (value === 'true') return true; if (value === 'false') return false; throw new Error('Invalid boolean value'); }, Renderer: (param)=>{ let { value } = param; return /*#__PURE__*/ jsx(Fragment, { children: value ? 'true' : 'false' }); } }); const displayOptions = { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }; const dateType = defineEasyType({ is: (value)=>value instanceof Date, type: 'date', colorKey: 'base0D', Renderer: (param)=>{ let { value } = param; return /*#__PURE__*/ jsx(Fragment, { children: value.toLocaleTimeString('en-us', displayOptions) }); } }); const functionBody = (func)=>{ const funcString = func.toString(); let isUsualFunction = true; const parenthesisPos = funcString.indexOf(')'); const arrowPos = funcString.indexOf('=>'); if (arrowPos !== -1 && arrowPos > parenthesisPos) { isUsualFunction = false; } if (isUsualFunction) { return funcString.substring(funcString.indexOf('{', parenthesisPos) + 1, funcString.lastIndexOf('}')); } return funcString.substring(funcString.indexOf('=>') + 2); }; const functionName = (func)=>{ const funcString = func.toString(); const isUsualFunction = funcString.indexOf('function') !== -1; if (isUsualFunction) { return funcString.substring(8, funcString.indexOf('{')).trim(); } return funcString.substring(0, funcString.indexOf('=>') + 2).trim(); }; const lb = '{'; const rb = '}'; const PreFunctionType = (props)=>{ return /*#__PURE__*/ jsxs(NoSsr, { children: [ /*#__PURE__*/ jsx(DataTypeLabel, { dataType: "function" }), /*#__PURE__*/ jsxs(Box, { component: "span", className: "data-function-start", sx: { letterSpacing: 0.5 }, children: [ functionName(props.value), ' ', lb ] }) ] }); }; const PostFunctionType = ()=>{ return /*#__PURE__*/ jsx(NoSsr, { children: /*#__PURE__*/ jsx(Box, { component: "span", className: "data-function-end", children: rb }) }); }; const FunctionType = (props)=>{ const functionColor = useJsonViewerStore((store)=>store.colorspace.base05); return /*#__PURE__*/ jsx(NoSsr, { children: /*#__PURE__*/ jsx(Box, { className: "data-function", sx: { display: props.inspect ? 'block' : 'inline-block', pl: props.inspect ? 2 : 0, color: functionColor }, children: props.inspect ? functionBody(props.value) : /*#__PURE__*/ jsx(Box, { component: "span", className: "data-function-body", onClick: ()=>props.setInspect(true), sx: { '&:hover': { cursor: 'pointer' }, padding: 0.5 }, children: "…" }) }) }); }; const functionType = { is: (value)=>typeof value === 'function', Component: FunctionType, PreComponent: PreFunctionType, PostComponent: PostFunctionType }; const nullType = defineEasyType({ is: (value)=>value === null, type: 'null', colorKey: 'base08', displayTypeLabel: false, Renderer: ()=>{ const backgroundColor = useJsonViewerStore((store)=>store.colorspace.base02); return /*#__PURE__*/ jsx(Box, { sx: { fontSize: '0.8rem', backgroundColor, fontWeight: 'bold', borderRadius: '3px', padding: '0.5px 2px' }, children: "NULL" }); } }); const isInt = (n)=>n % 1 === 0; const nanType = defineEasyType({ is: (value)=>typeof value === 'number' && isNaN(value), type: 'NaN', colorKey: 'base08', displayTypeLabel: false, serialize: ()=>'NaN', // allow deserialize the value back to number deserialize: (value)=>parseFloat(value), Renderer: ()=>{ const backgroundColor = useJsonViewerStore((store)=>store.colorspace.base02); return /*#__PURE__*/ jsx(Box, { sx: { backgroundColor, fontSize: '0.8rem', fontWeight: 'bold', borderRadius: '3px', padding: '0.5px 2px' }, children: "NaN" }); } }); const floatType = defineEasyType({ is: (value)=>typeof value === 'number' && !isInt(value) && !isNaN(value), type: 'float', colorKey: 'base0B', serialize: (value)=>value.toString(), deserialize: (value)=>parseFloat(value), Renderer: (param)=>{ let { value } = param; return /*#__PURE__*/ jsx(Fragment, { children: value }); } }); const intType = defineEasyType({ is: (value)=>typeof value === 'number' && isInt(value), type: 'int', colorKey: 'base0F', serialize: (value)=>value.toString(), // allow deserialize the value to float deserialize: (value)=>parseFloat(value), Renderer: (param)=>{ let { value } = param; return /*#__PURE__*/ jsx(Fragment, { children: value }); } }); const bigIntType = defineEasyType({ is: (value)=>typeof value === 'bigint', type: 'bigint', colorKey: 'base0F', serialize: (value)=>value.toString(), deserialize: (value)=>BigInt(value.replace(/\D/g, '')), Renderer: (param)=>{ let { value } = param; return /*#__PURE__*/ jsx(Fragment, { children: "".concat(value, "n") }); } }); const BaseIcon = (param)=>{ let { d, ...props } = param; return /*#__PURE__*/ jsx(SvgIcon, { ...props, children: /*#__PURE__*/ jsx("path", { d: d }) }); }; const AddBox = 'M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m0 16H5V5h14zm-8-2h2v-4h4v-2h-4V7h-2v4H7v2h4z'; const Check = 'M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'; const ChevronRight = 'M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'; const CircularArrows = 'M 12 2 C 10.615 1.998 9.214625 2.2867656 7.890625 2.8847656 L 8.9003906 4.6328125 C 9.9043906 4.2098125 10.957 3.998 12 4 C 15.080783 4 17.738521 5.7633175 19.074219 8.3222656 L 17.125 9 L 21.25 11 L 22.875 7 L 20.998047 7.6523438 C 19.377701 4.3110398 15.95585 2 12 2 z M 6.5097656 4.4882812 L 2.2324219 5.0820312 L 3.734375 6.3808594 C 1.6515335 9.4550558 1.3615962 13.574578 3.3398438 17 C 4.0308437 18.201 4.9801562 19.268234 6.1601562 20.115234 L 7.1699219 18.367188 C 6.3019219 17.710187 5.5922656 16.904 5.0722656 16 C 3.5320014 13.332354 3.729203 10.148679 5.2773438 7.7128906 L 6.8398438 9.0625 L 6.5097656 4.4882812 z M 19.929688 13 C 19.794687 14.08 19.450734 15.098 18.927734 16 C 17.386985 18.668487 14.531361 20.090637 11.646484 19.966797 L 12.035156 17.9375 L 8.2402344 20.511719 L 10.892578 23.917969 L 11.265625 21.966797 C 14.968963 22.233766 18.681899 20.426323 20.660156 17 C 21.355156 15.801 21.805219 14.445 21.949219 13 L 19.929688 13 z'; const Close = 'M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'; const ContentCopy = 'M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'; const Edit = 'M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'; const ExpandMore = 'M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z'; const Delete = 'M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM8 9h8v10H8zm7.5-5l-1-1h-5l-1 1H5v2h14V4z'; const AddBoxIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: AddBox, ...props }); }; const CheckIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: Check, ...props }); }; const ChevronRightIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: ChevronRight, ...props }); }; const CircularArrowsIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: CircularArrows, ...props }); }; const CloseIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: Close, ...props }); }; const ContentCopyIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: ContentCopy, ...props }); }; const EditIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: Edit, ...props }); }; const ExpandMoreIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: ExpandMore, ...props }); }; const DeleteIcon = (props)=>{ return /*#__PURE__*/ jsx(BaseIcon, { d: Delete, ...props }); }; const objectLb = '{'; const arrayLb = '['; const objectRb = '}'; const arrayRb = ']'; function inspectMetadata(value) { const length = getValueSize(value); let name = ''; if (value instanceof Map || value instanceof Set) { name = value[Symbol.toStringTag]; } if (Object.prototype.hasOwnProperty.call(value, Symbol.toStringTag)) { name = value[Symbol.toStringTag]; } const itemsPluralized = length === 1 ? 'Item' : 'Items'; return "".concat(length, " ").concat(itemsPluralized).concat(name ? " (".concat(name, ")") : ''); } const PreObjectType = (props)=>{ const metadataColor = useJsonViewerStore((store)=>store.colorspace.base04); const textColor = useTextColor(); const isArrayLike = useMemo(()=>Array.isArray(props.value) || props.value instanceof Set, [ props.value ]); const isEmptyValue = useMemo(()=>getValueSize(props.value) === 0, [ props.value ]); const sizeOfValue = useMemo(()=>inspectMetadata(props.value), [ props.value ]); const displaySize = useJsonViewerStore((store)=>store.displaySize); const shouldDisplaySize = useMemo(()=>typeof displaySize === 'function' ? displaySize(props.path, props.value) : displaySize, [ displaySize, props.path, props.value ]); const isTrap = useIsCycleReference(props.path, props.value); return /*#__PURE__*/ jsxs(Box, { component: "span", className: "data-object-start", sx: { letterSpacing: 0.5 }, children: [ isArrayLike ? arrayLb : objectLb, shouldDisplaySize && props.inspect && !isEmptyValue && /*#__PURE__*/ jsx(Box, { component: "span", sx: { pl: 0.5, fontStyle: 'italic', color: metadataColor, userSelect: 'none' }, children: sizeOfValue }), isTrap && !props.inspect && /*#__PURE__*/ jsxs(Fragment, { children: [ /*#__PURE__*/ jsx(CircularArrowsIcon, { sx: { fontSize: 12, color: textColor, mx: 0.5 } }), /*#__PURE__*/ jsx(DataBox, { sx: { cursor: 'pointer', userSelect: 'none' }, children: isTrap }) ] }) ] }); }; const PostObjectType = (props)=>{ const metadataColor = useJsonViewerStore((store)=>store.colorspace.base04); const textColor = useTextColor(); const isArrayLike = useMemo(()=>Array.isArray(props.value) || props.value instanceof Set, [ props.value ]); const isEmptyValue = useMemo(()=>getValueSize(props.value) === 0, [ props.value ]); const sizeOfValue = useMemo(()=>inspectMetadata(props.value), [ props.value ]); const displaySize = useJsonViewerStore((store)=>store.displaySize); const shouldDisplaySize = useMemo(()=>typeof displaySize === 'function' ? displaySize(props.path, props.value) : displaySize, [ displaySize, props.path, props.value ]); return /*#__PURE__*/ jsxs(Box, { component: "span", className: "data-object-end", sx: { lineHeight: 1.5, color: textColor, letterSpacing: 0.5, opacity: 0.8 }, children: [ isArrayLike ? arrayRb : objectRb, shouldDisplaySize && (isEmptyValue || !props.inspect) ? /*#__PURE__*/ jsx(Box, { component: "span", sx: { pl: 0.5, fontStyle: 'italic', color: metadataColor, userSelect: 'none' }, children: sizeOfValue }) : null ] }); }; function getIterator(value) { return typeof (value === null || value === void 0 ? void 0 : value[Symbol.iterator]) === 'function'; } const ObjectType = (props)=>{ const keyColor = useTextColor(); const borderColor = useJsonViewerStore((store)=>store.colorspace.base02); const groupArraysAfterLength = useJsonViewerStore((store)=>store.groupArraysAfterLength); const isTrap = useIsCycleReference(props.path, props.value); const [displayLength, setDisplayLength] = useState(useJsonViewerStore((store)=>store.maxDisplayLength)); const objectSortKeys = useJsonViewerStore((store)=>store.objectSortKeys); const elements = useMemo(()=>{ if (!props.inspect) { return null; } const value = props.value; const iterator = getIterator(value); // Array also has iterator, we skip it and treat it as an array as normal. if (iterator && !Array.isArray(value)) { const elements = []; if (value instanceof Map) { const lastIndex = value.size - 1; let index = 0; value.forEach((value, k)=>{ // fixme: key might be a object, array, or any value for the `Map<any, any>` const key = k.toString(); const path = [ ...props.path, key ]; elements.push(/*#__PURE__*/ jsx(DataKeyPair, { path: path, value: value, prevValue: props.prevValue instanceof Map ? props.prevValue.get(k) : undefined, editable: false, last: index === lastIndex }, key)); index++; }); } else { // iterate with iterator func const iterator = value[Symbol.iterator](); let result = iterator.next(); let count = 0; while(true){ const nextResult = iterator.next(); var _nextResult_done; elements.push(/*#__PURE__*/ jsx(DataKeyPair, { path: [ ...props.path, "iterator:".concat(count) ], value: result.value, nestedIndex: count, editable: false, last: (_nextResult_done = nextResult.done) !== null && _nextResult_done !== void 0 ? _nextResult_done : false }, count)); if (nextResult.done) { break; } count++; result = nextResult; } } return elements; } if (Array.isArray(value)) { const lastIndex = value.length - 1; // unknown[] if (value.length <= groupArraysAfterLength) { const elements = value.slice(0, displayLength).map((value, _index)=>{ const index = props.nestedIndex ? props.nestedIndex * groupArraysAfterLength + _index : _index; const path = [ ...props.path, index ]; return /*#__PURE__*/ jsx(DataKeyPair, { path: path, value: value, prevValue: Array.isArray(props.prevValue) ? props.prevValue[index] : undefined, last: _index === lastIndex }, index); }); if (value.length > displayLength) { const rest = value.length - displayLength; elements.push(/*#__PURE__*/ jsxs(DataBox, { sx: { cursor: 'pointer', lineHeight: 1.5, color: keyColor, letterSpacing: 0.5, opacity: 0.8, userSelect: 'none' }, onClick: ()=>setDisplayLength((length)=>length * 2), children: [ "hidden ", rest, " items…" ] }, "last")); } return elements; } const elements = segmentArray(value, groupArraysAfterLength); const prevElements = Array.isArray(props.prevValue) ? segmentArray(props.prevValue, groupArraysAfterLength) : undefined; const elementsLastIndex = elements.length - 1; return elements.map((list, index)=>{ return /*#__PURE__*/ jsx(DataKeyPair, { path: props.path, value: list, nestedIndex: index, prevValue: prevElements === null || prevElements === void 0 ? void 0 : prevElements[index], last: index === elementsLastIndex }, index); }); } // object let entries = Object.entries(value); if (objectSortKeys) { entries = objectSortKeys === true ? entries.sort((param, param1)=>{ let [a] = param, [b] = param1; return a.localeCompare(b); }) : entries.sort((param, param1)=>{ let [a] = param, [b] = param1; return objectSortKeys(a, b); }); } const lastIndex = entries.length - 1; const elements = entries.slice(0, displayLength).map((param, index)=>{ let [key, value] = param; var _props_prevValue; const path = [ ...props.path, key ]; return /*#__PURE__*/ jsx(DataKeyPair, { path: path, value: value, prevValue: (_props_prevValue = props.prevValue) === null || _props_prevValue === void 0 ? void 0 : _props_prevValue[key], last: index === lastIndex }, key); }); if (entries.length > displayLength) { const rest = entries.length - displayLength; elements.push(/*#__PURE__*/ jsxs(DataBox, { sx: { cursor: 'pointer', lineHeight: 1.5, color: keyColor, letterSpacing: 0.5, opacity: 0.8, userSelect: 'none' }, onClick: ()=>setDisplayLength((length)=>length * 2), children: [ "hidden ", rest, " items…" ] }, "last")); } return elements; }, [ props.inspect, props.value, props.prevValue, props.path, props.nestedIndex, groupArraysAfterLength, displayLength, keyColor, objectSortKeys ]); const marginLeft = props.inspect ? 0.6 : 0; const width = useJsonViewerStore((store)=>store.indentWidth); const indentWidth = props.inspect ? width - marginLeft : width; const isEmptyValue = useMemo(()=>getValueSize(props.value) === 0, [ props.value ]); if (isEmptyValue) { return null; } return /*#__PURE__*/ jsx(Box, { className: "data-object", sx: { display: props.inspect ? 'block' : 'inline-block', pl: props.inspect ? indentWidth - 0.6 : 0, marginLeft, color: keyColor, borderLeft: props.inspect ? "1px solid ".concat(borderColor) : 'none' }, children: props.inspect ? elements : !isTrap && /*#__PURE__*/ jsx(Box, { component: "span", className: "data-object-body", onClick: ()=>props.setInspect(true), sx: { '&:hover': { cursor: 'pointer' }, padding: 0.5, userSelect: 'none' }, children: "…" }) }); }; const objectType = { is: (value)=>typeof value === 'object', Component: ObjectType, PreComponent: PreObjectType, PostComponent: PostObjectType }; const stringType = defineEasyType({ is: (value)=>typeof value === 'string', type: 'string', colorKey: 'base09', serialize: (value)=>value, deserialize: (value)=>value, Renderer: (props)=>{ const [showRest, setShowRest] = useState(false); const collapseStringsAfterLength = useJsonViewerStore((store)=>store.collapseStringsAfterLength); const value = showRest ? props.value : props.value.slice(0, collapseStringsAfterLength); const hasRest = props.value.length > collapseStringsAfterLength; return /*#__PURE__*/ jsxs(Box, { component: "span", sx: { overflowWrap: 'anywhere', cursor: hasRest ? 'pointer' : 'inherit' }, onClick: ()=>{ var _window_getSelection; if (((_window_getSelection = window.getSelection()) === null || _window_getSelection === void 0 ? void 0 : _window_getSelection.type) === 'Range') { return; } if (hasRest) { setShowRest((value)=>!value); } }, children: [ '"', value, hasRest && !showRest && /*#__PURE__*/ jsx(Box, { component: "span", sx: { padding: 0.5 }, children: "…" }), '"' ] }); } }); const undefinedType = defineEasyType({ is: (value)=>value === undefined, type: 'undefined', colorKey: 'base05', displayTypeLabel: false, Renderer: ()=>{ const backgroundColor = useJsonViewerStore((store)=>store.colorspace.base02); return /*#__PURE__*/ jsx(Box, { sx: { fontSize: '0.7rem', backgroundColor, borderRadius: '3px', padding: '0.5px 2px' }, children: "undefined" }); } }); function memorizeDataType(dataType) { function compare(prevProps, nextProps) { var _prevProps_path, _nextProps_path; return Object.is(prevProps.value, nextProps.value) && prevProps.inspect && nextProps.inspect && ((_prevProps_path = prevProps.path) === null || _prevProps_path === void 0 ? void 0 : _prevProps_pat