diff --git a/Cargo.lock b/Cargo.lock index 72916e3..909cb62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "haste" version = "0.0.0" -source = "git+https://github.com/blukai/haste.git?rev=7a2bdf91d4ebea8fa0056900c1319964855206f1#7a2bdf91d4ebea8fa0056900c1319964855206f1" +source = "git+https://github.com/blukai/haste.git?rev=66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b#66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b" dependencies = [ "haste_core", ] @@ -156,7 +156,7 @@ dependencies = [ [[package]] name = "haste_core" version = "0.0.0" -source = "git+https://github.com/blukai/haste.git?rev=7a2bdf91d4ebea8fa0056900c1319964855206f1#7a2bdf91d4ebea8fa0056900c1319964855206f1" +source = "git+https://github.com/blukai/haste.git?rev=66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b#66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b" dependencies = [ "dungers", "dyn-clone", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "haste_protos" version = "0.0.0" -source = "git+https://github.com/blukai/haste.git?rev=7a2bdf91d4ebea8fa0056900c1319964855206f1#7a2bdf91d4ebea8fa0056900c1319964855206f1" +source = "git+https://github.com/blukai/haste.git?rev=66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b#66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b" dependencies = [ "prost", "prost-build", @@ -184,7 +184,7 @@ dependencies = [ [[package]] name = "haste_vartype" version = "0.0.0" -source = "git+https://github.com/blukai/haste.git?rev=7a2bdf91d4ebea8fa0056900c1319964855206f1#7a2bdf91d4ebea8fa0056900c1319964855206f1" +source = "git+https://github.com/blukai/haste.git?rev=66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b#66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b" dependencies = [ "dungers", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 94a1839..f5efb24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -haste = { git = "https://github.com/blukai/haste.git", rev = "7a2bdf91d4ebea8fa0056900c1319964855206f1", features = ["preserve-metadata"] } +haste = { git = "https://github.com/blukai/haste.git", rev = "66300ee4bbb60e9dbfd8c94e55498bfbe7ed555b", features = ["preserve-metadata"] } wasm-bindgen = "0.2" [lib] diff --git a/src/AppBar.tsx b/src/AppBar.tsx index 9705f1d..6a0202e 100644 --- a/src/AppBar.tsx +++ b/src/AppBar.tsx @@ -6,11 +6,15 @@ import { MoonIcon, SunIcon, XIcon, + SlashIcon, + ChevronDownIcon, } from "lucide-react"; -import { useLayoutEffect } from "react"; -import { darkModeAtom, demFileAtom, fullWidthAtom } from "./atoms"; +import { useLayoutEffect, useState } from "react"; +import { darkModeAtom, demFileAtom, demViewAtom, fullWidthAtom } from "./atoms"; import { Button } from "./lib/Button"; import { Tooltip } from "./lib/Tooltip"; +import * as DropdownMenu from "./lib/DropdownMenu"; +import { cn } from "./lib/style"; function IconSection() { return ( @@ -32,6 +36,47 @@ function IconSection() { ); } +function DemViewSelection() { + const [demView, setDemView] = useAtom(demViewAtom); + const [demViewOpen, setDemViewOpen] = useState(false); + + return ( + + + + + + + setDemView("entities")} + > + entities + + setDemView("baselineEntities")} + > + baseline entities + + + + + ); +} + function DemFileSection() { const [demFile, setDemFile] = useAtom(demFileAtom); @@ -39,16 +84,17 @@ function DemFileSection() { // files that were open. return ( -
+
{demFile && ( <> - - {demFile.name} - +
+ {demFile.name} + +
+ + )}
@@ -118,7 +164,7 @@ function UiSection() { export default function AppBar() { return ( -
+
diff --git a/src/DemEntities.tsx b/src/DemEntities.tsx index 3d9dc76..6308690 100644 --- a/src/DemEntities.tsx +++ b/src/DemEntities.tsx @@ -1,4 +1,3 @@ -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; import { useVirtualizer } from "@tanstack/react-virtual"; import { useAtom } from "jotai"; import { CheckIcon, CogIcon, Link2Icon, Link2OffIcon } from "lucide-react"; @@ -9,12 +8,19 @@ import { demParserAtom, demSelectedEntityIndexAtom, demTickAtom, + demViewAtom, } from "./atoms"; import { Button } from "./lib/Button"; import { ScrollArea } from "./lib/ScrollArea"; import { Tooltip } from "./lib/Tooltip"; -import { type EntityLi, handleToIndex, isHandleValid } from "./lib/haste"; +import { + type EntityLi, + handleToIndex, + isHandleValid, + EntityFieldLi, +} from "./lib/haste"; import { cn } from "./lib/style"; +import * as DropdownMenu from "./lib/DropdownMenu"; const LI_HEIGHT = 26; @@ -24,30 +30,6 @@ const DEFAULT_SHOW_FIELD_ENCODED_TYPE = true; const DEFAULT_SHOW_FIELD_DECODED_TYPE = false; const DEFAULT_SHOW_FIELD_PATH = false; -type DropdownMenuCheckboxItemProps = Pick< - DropdownMenuPrimitive.DropdownMenuCheckboxItemProps, - "checked" | "onCheckedChange" -> & { - label: React.ReactNode; -}; - -function DropdownMenuCheckboxItem(props: DropdownMenuCheckboxItemProps) { - const { checked, onCheckedChange, label } = props; - - return ( - - - - - {label} - - ); -} - type EntityListPreferencesProps = { showEntityIndex: boolean; setShowEntityIndex: (value: boolean) => void; @@ -55,53 +37,56 @@ type EntityListPreferencesProps = { // NOTE: keep this in sync with EntityFieldListPreferences function EntityListPreferences(props: EntityListPreferencesProps) { - const { - showEntityIndex, - setShowEntityIndex, - } = props; + const { showEntityIndex, setShowEntityIndex } = props; const [open, setOpen] = useState(false); - const active = open; - return ( - - + + - - - - + + - - - - + > + entity index + + + + ); } function EntityList() { const [demParser] = useAtom(demParserAtom); + const [demView] = useAtom(demViewAtom); const [demTick] = useAtom(demTickAtom); const entityList = useMemo(() => { demTick; // trick eslint - return demParser?.listEntities(); - }, [demParser, demTick]); + + let entityList: EntityLi[] | undefined; + if (demView === "entities") { + entityList = demParser?.listEntities(); + } else if (demView === "baselineEntities") { + entityList = demParser?.listBaselineEntities(); + } + + return entityList; + }, [demParser, demView, demTick]); const [, startTransition] = useTransition(); const [filteredEntityList, setFinalEntityList] = useState(entityList); @@ -142,7 +127,9 @@ function EntityList() { [setDemSelectedEntityIndex], ); - const [showEntityIndex, setShowEntityIndex] = useState(DEFAULT_SHOW_ENTITY_INDEX); + const [showEntityIndex, setShowEntityIndex] = useState( + DEFAULT_SHOW_ENTITY_INDEX, + ); return (
@@ -231,8 +218,8 @@ function EntityFieldListPreferences(props: EntityFieldListPreferencesProps) { const active = open; return ( - - + + - - - + + - - + field path + + - + encoded type + + - - - + > + decoded type + + + + ); } function EntityFieldList() { const [demParser] = useAtom(demParserAtom); - const [demTick] = useAtom(demTickAtom); + const [demView] = useAtom(demViewAtom); const [demSelectedEntityIndex] = useAtom(demSelectedEntityIndexAtom); + const [demTick] = useAtom(demTickAtom); const { entityFieldList, joinedPathMaxLen } = useMemo(() => { demTick; // trick eslint @@ -283,21 +274,28 @@ function EntityFieldList() { return {}; } + let tmpEntityFieldList: EntityFieldLi[] | undefined; + if (demView === "entities") { + tmpEntityFieldList = demParser?.listEntityFields(demSelectedEntityIndex); + } else if (demView === "baselineEntities") { + tmpEntityFieldList = demParser?.listBaselineEntityFields( + demSelectedEntityIndex, + ); + } + let joinedPathMaxLen = 0; - const entityFieldList = demParser - ?.listEntityFields(demSelectedEntityIndex) - ?.map((entityField) => { - const joinedPath = Array.from(entityField.path) - .map((part) => part.toString().padStart(4, " ")) - .join(""); - joinedPathMaxLen = Math.max(joinedPathMaxLen, joinedPath.length); - return { - inner: entityField, - joinedPath, - joinedNamedPath: entityField.namedPath.join("."), - }; - }); + const entityFieldList = tmpEntityFieldList?.map((entityField) => { + const joinedPath = Array.from(entityField.path) + .map((part) => part.toString().padStart(4, " ")) + .join(""); + joinedPathMaxLen = Math.max(joinedPathMaxLen, joinedPath.length); + return { + inner: entityField, + joinedPath, + joinedNamedPath: entityField.namedPath.join("."), + }; + }); entityFieldList?.sort((a, b) => { // compare path arrays element by element @@ -316,7 +314,7 @@ function EntityFieldList() { }); return { entityFieldList, joinedPathMaxLen }; - }, [demSelectedEntityIndex, demParser, demTick]); + }, [demParser, demView, demSelectedEntityIndex, demTick]); type WrappedEntityFieldLi = NonNullable[0]; diff --git a/src/atoms.ts b/src/atoms.ts index 52c6af1..9dd51a0 100644 --- a/src/atoms.ts +++ b/src/atoms.ts @@ -26,3 +26,4 @@ export const demFileAtom = atom(undefined); export const demParserAtom = atom(undefined); export const demTickAtom = atom(0); export const demSelectedEntityIndexAtom = atom(undefined); +export const demViewAtom = atom<"entities" | "baselineEntities">("entities"); diff --git a/src/lib/DropdownMenu.tsx b/src/lib/DropdownMenu.tsx new file mode 100644 index 0000000..c25fca1 --- /dev/null +++ b/src/lib/DropdownMenu.tsx @@ -0,0 +1,30 @@ +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import React from "react"; +import { cn } from "./style"; +import { CheckIcon } from "lucide-react"; + +export * from "@radix-ui/react-dropdown-menu"; + +export const CheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => { + const { className, checked, children, ...restProps } = props; + + return ( + + + + + {children} + + ); +}); diff --git a/src/lib/haste/haste.d.ts b/src/lib/haste/haste.d.ts index 8553391..b99efe8 100644 --- a/src/lib/haste/haste.d.ts +++ b/src/lib/haste/haste.d.ts @@ -66,10 +66,19 @@ export class WrappedParser { */ listEntities(): (EntityLi)[] | undefined; /** +* @returns {(EntityLi)[] | undefined} +*/ + listBaselineEntities(): (EntityLi)[] | undefined; +/** * @param {number} entity_index * @returns {(EntityFieldLi)[] | undefined} */ listEntityFields(entity_index: number): (EntityFieldLi)[] | undefined; +/** +* @param {number} entity_index +* @returns {(EntityFieldLi)[] | undefined} +*/ + listBaselineEntityFields(entity_index: number): (EntityFieldLi)[] | undefined; } export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; @@ -97,7 +106,9 @@ export interface InitOutput { readonly wrappedparser_totalTicks: (a: number, b: number) => void; readonly wrappedparser_runToTick: (a: number, b: number, c: number) => void; readonly wrappedparser_listEntities: (a: number, b: number) => void; + readonly wrappedparser_listBaselineEntities: (a: number, b: number) => void; readonly wrappedparser_listEntityFields: (a: number, b: number, c: number) => void; + readonly wrappedparser_listBaselineEntityFields: (a: number, b: number, c: number) => void; readonly isHandleValid: (a: number) => number; readonly handleToIndex: (a: number) => number; readonly __wbg_set_entityli_name: (a: number, b: number, c: number) => void; diff --git a/src/lib/haste/haste.js b/src/lib/haste/haste.js index 51b97eb..1f1b956 100644 --- a/src/lib/haste/haste.js +++ b/src/lib/haste/haste.js @@ -491,6 +491,25 @@ export class WrappedParser { } } /** + * @returns {(EntityLi)[] | undefined} + */ + listBaselineEntities() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.wrappedparser_listBaselineEntities(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + let v1; + if (r0 !== 0) { + v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + } + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** * @param {number} entity_index * @returns {(EntityFieldLi)[] | undefined} */ @@ -510,6 +529,26 @@ export class WrappedParser { wasm.__wbindgen_add_to_stack_pointer(16); } } + /** + * @param {number} entity_index + * @returns {(EntityFieldLi)[] | undefined} + */ + listBaselineEntityFields(entity_index) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.wrappedparser_listBaselineEntityFields(retptr, this.__wbg_ptr, entity_index); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + let v1; + if (r0 !== 0) { + v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + } + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } } async function __wbg_load(module, imports) { @@ -550,14 +589,14 @@ function __wbg_get_imports() { const ret = new Error(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }; - imports.wbg.__wbg_entityli_new = function(arg0) { - const ret = EntityLi.__wrap(arg0); - return addHeapObject(ret); - }; imports.wbg.__wbg_entityfieldli_new = function(arg0) { const ret = EntityFieldLi.__wrap(arg0); return addHeapObject(ret); }; + imports.wbg.__wbg_entityli_new = function(arg0) { + const ret = EntityLi.__wrap(arg0); + return addHeapObject(ret); + }; imports.wbg.__wbindgen_string_new = function(arg0, arg1) { const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); diff --git a/src/lib/haste/haste.rs b/src/lib/haste/haste.rs index 2a57875..0eb898f 100644 --- a/src/lib/haste/haste.rs +++ b/src/lib/haste/haste.rs @@ -6,7 +6,7 @@ // - https://stackoverflow.com/questions/65332927/is-it-possible-to-wasm-bindgen-public-structs-and-functions-defined-in-anothe use haste::{ - entities, + entities::{self, Entity}, fieldpath::FieldPath, fieldvalue::FieldValue, flattenedserializers::FlattenedSerializer, @@ -66,44 +66,69 @@ impl WrappedParser { .map_err(|err| JsError::new(&err.to_string())) } + fn collect_entity_list<'a>( + entities: impl Iterator, + ) -> Vec { + entities + .map(|(index, entity)| EntityLi { + index: *index, + name: entity.serializer().serializer_name.str.to_string(), + }) + .collect() + } + #[wasm_bindgen(js_name = "listEntities")] pub fn list_entities(&self) -> Option> { - self.parser.entities().map(|entities| { - entities - .iter() - .map(|(index, entity)| EntityLi { - index: *index, - name: entity.serializer().serializer_name.str.to_string(), - }) - .collect() - }) + self.parser + .entities() + .map(|entities| Self::collect_entity_list(entities.iter())) + } + + #[wasm_bindgen(js_name = "listBaselineEntities")] + pub fn list_baseline_entities(&self) -> Option> { + self.parser + .entities() + .map(|entities| Self::collect_entity_list(entities.iter_baselines())) + } + + fn collect_entity_field_list(entity: &Entity) -> Vec { + entity + .iter() + .map(|(key, field_value)| { + let fp = entity + .get_path(key) + // NOTE: this should never throw because if entity + // was returned it means that it exists thus path + // exists + .unwrap_throw(); + let (named_path, var_type) = get_value_info(entity.serializer(), fp); + + EntityFieldLi { + path: fp.iter().cloned().collect(), + named_path, + value: field_value.to_string(), + encoded_as: var_type, + decoded_as: get_field_value_discriminant_name(field_value).to_string(), + } + }) + .collect() } #[wasm_bindgen(js_name = "listEntityFields")] pub fn list_entity_fields(&self, entity_index: i32) -> Option> { self.parser.entities().and_then(|entities| { - entities.get(&entity_index).map(|entity| { - entity - .iter() - .map(|(key, field_value)| { - let fp = entity - .get_path(key) - // NOTE: this should never throw because if entity - // was returned it means that it exists thus path - // exists - .unwrap_throw(); - let (named_path, var_type) = get_value_info(entity.serializer(), fp); - - EntityFieldLi { - path: fp.iter().cloned().collect(), - named_path, - value: field_value.to_string(), - encoded_as: var_type, - decoded_as: get_field_value_discriminant_name(field_value).to_string(), - } - }) - .collect() - }) + entities + .get(&entity_index) + .map(Self::collect_entity_field_list) + }) + } + + #[wasm_bindgen(js_name = "listBaselineEntityFields")] + pub fn list_baseline_entity_fields(&self, entity_index: i32) -> Option> { + self.parser.entities().and_then(|entities| { + entities + .get_baseline(&entity_index) + .map(Self::collect_entity_field_list) }) } } diff --git a/src/lib/haste/haste_bg.wasm b/src/lib/haste/haste_bg.wasm index 2ba3c5b..79da4fd 100644 Binary files a/src/lib/haste/haste_bg.wasm and b/src/lib/haste/haste_bg.wasm differ diff --git a/src/lib/haste/haste_bg.wasm.d.ts b/src/lib/haste/haste_bg.wasm.d.ts index 642b840..9fd3f90 100644 --- a/src/lib/haste/haste_bg.wasm.d.ts +++ b/src/lib/haste/haste_bg.wasm.d.ts @@ -22,7 +22,9 @@ export function wrappedparser_tick(a: number): number; export function wrappedparser_totalTicks(a: number, b: number): void; export function wrappedparser_runToTick(a: number, b: number, c: number): void; export function wrappedparser_listEntities(a: number, b: number): void; +export function wrappedparser_listBaselineEntities(a: number, b: number): void; export function wrappedparser_listEntityFields(a: number, b: number, c: number): void; +export function wrappedparser_listBaselineEntityFields(a: number, b: number, c: number): void; export function isHandleValid(a: number): number; export function handleToIndex(a: number): number; export function __wbg_set_entityli_name(a: number, b: number, c: number): void; diff --git a/tailwind.config.js b/tailwind.config.js index 49a09bb..fa889b8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -9,7 +9,7 @@ export default { fg: "rgb(var(--fg) / )", "bg-subtle": "rgb(var(--bg-subtle) / )", "fg-subtle": "rgb(var(--fg-subtle) / )", - divider: "#404040", // dark neutral-700 + divider: "#525252", // dark neutral-600 }, }, },