Compare commits

...

6 Commits

Author SHA1 Message Date
Trisha Lim
96974fe49d add changeset 2025-03-21 20:32:42 +07:00
Trisha Lim
8ebb837af6 fix unreadable text 2025-03-21 20:10:36 +07:00
Trisha Lim
b1225ead61 wrap all classnames with tw and clsx 2025-03-21 20:07:36 +07:00
Giordano Ricci
7d77d4c8a8 working twind 2025-03-21 11:39:19 +00:00
Trisha Lim
7394b3b636 enable hash, use setup instead of install 2025-03-21 16:58:57 +07:00
Trisha Lim
ca9327a60f disable renaming tailwind classes when using jazz-inspector 2025-03-20 21:44:17 +07:00
19 changed files with 231 additions and 101 deletions

View File

@@ -0,0 +1,6 @@
---
"jazz-inspector": patch
"jazz-inspector-app": patch
---
isolate class name hashing to inspector

View File

@@ -223,6 +223,7 @@ function AccountSwitcher({
<div className="relative flex items-stretch gap-1">
<Select
label="Account to inspect"
hideLabel
className="label:sr-only max-w-96"
value={currentAccount?.id || "add-account"}
onChange={(e) => {

View File

@@ -1,3 +1,5 @@
import { classNames } from "./utils.js";
export function LinkIcon() {
return (
<svg
@@ -6,7 +8,7 @@ export function LinkIcon() {
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-3 h-3"
className={classNames("w-3 h-3")}
>
<path
strokeLinecap="round"

View File

@@ -28,6 +28,7 @@ Object.keys(stonePalette).forEach((key) => {
});
export default defineConfig({
hash: true,
presets: [presetAutoprefix(), presetTailwind()],
theme: {
extend: {

View File

@@ -1,4 +1,8 @@
import { install } from "@twind/core";
import { setup } from "@twind/core";
import config from "./twind.config";
install(config);
export const tw = setup(
config,
undefined,
document.getElementById("__jazz_inspector")!,
);

View File

@@ -1,5 +1,5 @@
import { clsx } from "clsx";
import { forwardRef } from "react";
import { classNames } from "../utils.js";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "tertiary" | "destructive" | "plain";
@@ -38,10 +38,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
"bg-red-600 border-red-600 text-white font-medium hover:bg-red-700 hover:border-red-700",
};
const classNames =
const classes =
variant === "plain"
? className
: clsx(
: classNames(
className,
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
"disabled:pointer-events-none disabled:opacity-70",
@@ -55,7 +55,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
ref={ref}
{...buttonProps}
disabled={disabled}
className={classNames}
className={classes}
type={type}
>
{children}

View File

@@ -9,6 +9,7 @@ import {
UserIcon,
XIcon,
} from "lucide-react";
import { classNames } from "../utils.js";
const icons = {
auth: UserIcon,
@@ -81,7 +82,7 @@ export function Icon({
size={sizes[size]}
strokeWidth={strokeWidths[size]}
strokeLinecap="round"
className={className}
className={classNames(className)}
{...svgProps}
/>
);

View File

@@ -1,6 +1,6 @@
import { clsx } from "clsx";
import { forwardRef, useId } from "react";
import { classNames } from "../utils.js";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
// label can be hidden with a "label:sr-only" className
label: string;
@@ -13,19 +13,19 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
const generatedId = useId();
const id = customId || generatedId;
const inputClassName = clsx(
const inputClassName = classNames(
"w-full rounded-md border px-3.5 py-2 shadow-sm",
"font-medium text-stone-900",
"dark:text-white dark:bg-stone-925",
);
const containerClassName = clsx("grid gap-1", className);
const containerClassName = classNames("grid gap-1", className);
return (
<div className={containerClassName}>
<label
htmlFor={id}
className={clsx(
className={classNames(
"text-stone-600 dark:text-stone-300",
hideLabel && "sr-only",
)}

View File

@@ -1,17 +1,20 @@
import { clsx } from "clsx";
import { useId } from "react";
import { classNames } from "../utils.js";
import { Icon } from "./icon.js";
export function Select(
props: React.SelectHTMLAttributes<HTMLSelectElement> & { label: string },
props: React.SelectHTMLAttributes<HTMLSelectElement> & {
label: string;
hideLabel?: boolean;
},
) {
const { label, id: customId, className } = props;
const { label, hideLabel, id: customId, className } = props;
const generatedId = useId();
const id = customId || generatedId;
const containerClassName = clsx("grid gap-1", className);
const containerClassName = classNames("grid gap-1", className);
const selectClassName = clsx(
const selectClassName = classNames(
"w-full rounded-md border pl-3.5 py-2 pr-8 shadow-sm",
"font-medium text-stone-900",
"dark:text-white dark:bg-stone-925",
@@ -20,19 +23,26 @@ export function Select(
);
return (
<div className={containerClassName}>
<label htmlFor={id} className="text-stone-600 dark:text-stone-300">
<div className={classNames(containerClassName)}>
<label
htmlFor={id}
className={classNames("text-stone-600 dark:text-stone-300", {
"sr-only": hideLabel,
})}
>
{label}
</label>
<div className="relative flex items-center">
<div className={classNames("relative flex items-center")}>
<select {...props} id={id} className={selectClassName}>
{props.children}
</select>
<Icon
name="chevronDown"
className="absolute right-[0.5em] text-stone-400 dark:text-stone-600"
className={classNames(
"absolute right-[0.5em] text-stone-400 dark:text-stone-600",
)}
size="sm"
/>
</div>

View File

@@ -0,0 +1,6 @@
import { ClassValue, clsx } from "clsx";
import { tw } from "./twind.js";
export const classNames = (...inputs: ClassValue[]) => {
return tw(clsx(inputs));
};

View File

@@ -2,6 +2,8 @@ import React from "react";
import { Button } from "../ui/button.js";
import { PageInfo } from "./types.js";
import { classNames } from "../utils.js";
interface BreadcrumbsProps {
path: PageInfo[];
onBreadcrumbClick: (index: number) => void;
@@ -12,10 +14,10 @@ export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
onBreadcrumbClick,
}) => {
return (
<div className="relative z-20 flex-1 flex items-center">
<div className={classNames("relative z-20 flex-1 flex items-center")}>
<Button
variant="plain"
className="text-blue px-1"
className={classNames("text-blue px-1 dark:text-blue-400")}
onClick={() => onBreadcrumbClick(-1)}
>
Home
@@ -25,13 +27,15 @@ export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
<React.Fragment key={page.coId}>
<span
aria-hidden
className="text-stone-400 dark:text-stone-600 px-0.5"
className={classNames(
"text-stone-400 dark:text-stone-600 px-0.5",
)}
>
/
</span>
<Button
variant="plain"
className="text-blue px-1"
className={classNames("text-blue px-1 dark:text-blue-400")}
onClick={() => onBreadcrumbClick(index)}
>
{index === 0 ? page.name || "Root" : page.name}

View File

@@ -13,6 +13,8 @@ import { Button } from "../ui/button.js";
import { PageInfo } from "./types.js";
import { AccountOrGroupPreview } from "./value-renderer.js";
import { classNames } from "../utils.js";
// typeguard for BinaryStreamStart
function isBinaryStreamStart(item: unknown): item is BinaryStreamStart {
return (
@@ -162,7 +164,7 @@ const LabelContentPair = ({
content: React.ReactNode;
}) => {
return (
<div className="flex flex-col gap-1.5">
<div className={classNames("flex flex-col gap-1.5")}>
<span>{label}</span>
<span>{content}</span>
</div>
@@ -220,12 +222,16 @@ function RenderCoBinaryStream({
const sizeInKB = (file.totalSize || 0) / 1024;
return (
<div className="mt-8 flex flex-col gap-8">
<div className="grid grid-cols-3 gap-2 max-w-3xl">
<div className={classNames("mt-8 flex flex-col gap-8")}>
<div className={classNames("grid grid-cols-3 gap-2 max-w-3xl")}>
<LabelContentPair
label="Mime Type"
content={
<span className="font-mono bg-gray-100 rounded px-2 py-1 text-sm dark:bg-stone-900">
<span
className={classNames(
"font-mono bg-gray-100 rounded px-2 py-1 text-sm dark:bg-stone-900",
)}
>
{mimeType || "No mime type"}
</span>
}
@@ -254,7 +260,9 @@ function RenderCoBinaryStream({
<LabelContentPair
label="Preview"
content={
<div className="bg-gray-50 dark:bg-gray-925 p-3 rounded">
<div
className={classNames("bg-gray-50 dark:bg-gray-925 p-3 rounded")}
>
<RenderBlobImage blob={blob} />
</div>
}
@@ -275,10 +283,12 @@ function RenderCoStream({
const userCoIds = streamPerUser.map((stream) => stream.split("_session")[0]);
return (
<div className="grid grid-cols-3 gap-2">
<div className={classNames("grid grid-cols-3 gap-2")}>
{userCoIds.map((id, idx) => (
<div
className="p-3 rounded-lg overflow-hidden border border-gray-200 cursor-pointer shadow-sm hover:bg-gray-100/5"
className={classNames(
"p-3 rounded-lg overflow-hidden border border-gray-200 cursor-pointer shadow-sm hover:bg-gray-100/5",
)}
key={id}
>
<AccountOrGroupPreview coId={id as CoID<RawCoValue>} node={node} />

View File

@@ -5,6 +5,8 @@ import { ResolveIcon } from "./type-icon.js";
import { PageInfo, isCoId } from "./types.js";
import { CoMapPreview, ValueRenderer } from "./value-renderer.js";
import { classNames } from "../utils.js";
export function GridView({
data,
onNavigate,
@@ -17,27 +19,41 @@ export function GridView({
const entries = Object.entries(data);
return (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
<div
className={classNames(
"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
)}
>
{entries.map(([key, child], childIndex) => (
<Button
variant="plain"
key={childIndex}
className={`p-3 text-left rounded-lg overflow-hidden transition-colors ${
isCoId(child)
? "border border-gray-200 shadow-sm hover:bg-gray-100/5"
: "bg-gray-50 dark:bg-gray-925 cursor-default"
}`}
className={classNames(
`p-3 text-left rounded-lg overflow-hidden transition-colors ${
isCoId(child)
? "border border-gray-200 shadow-sm hover:bg-gray-100/5"
: "bg-gray-50 dark:bg-gray-925 cursor-default"
}`,
)}
onClick={() =>
isCoId(child) &&
onNavigate([{ coId: child as CoID<RawCoValue>, name: key }])
}
>
<h3 className="overflow-hidden text-ellipsis whitespace-nowrap">
<h3
className={classNames(
"overflow-hidden text-ellipsis whitespace-nowrap",
)}
>
{isCoId(child) ? (
<span className="font-medium flex justify-between">
<span className={classNames("font-medium flex justify-between")}>
{key}
<div className="py-1 px-2 text-sm bg-gray-100 rounded dark:bg-gray-900">
<div
className={classNames(
"py-1 px-2 text-sm bg-gray-100 rounded dark:bg-gray-900",
)}
>
<ResolveIcon coId={child as CoID<RawCoValue>} node={node} />
</div>
</span>
@@ -45,7 +61,7 @@ export function GridView({
<span>{key}</span>
)}
</h3>
<div className="mt-2 text-sm">
<div className={classNames("mt-2 text-sm")}>
{isCoId(child) ? (
<CoMapPreview coId={child as CoID<RawCoValue>} node={node} />
) : (

View File

@@ -7,6 +7,8 @@ import { Breadcrumbs } from "./breadcrumbs.js";
import { PageStack } from "./page-stack.js";
import { usePagePath } from "./use-page-path.js";
import { classNames } from "../utils.js";
type Position =
| "bottom right"
| "bottom left"
@@ -43,15 +45,20 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
};
if (!open) {
// not sure if this will work, probably is better to use inline styles for the button, but please check.
return (
<Button
id="__jazz_inspector"
variant="secondary"
size="sm"
onClick={() => setOpen(true)}
className={`fixed w-10 h-10 bg-white shadow-sm bottom-0 right-0 m-4 p-1.5 ${positionClasses[position]}`}
className={classNames(
`fixed w-10 h-10 bg-white shadow-sm bottom-0 right-0 m-4 p-1.5 ${positionClasses[position]}`,
)}
>
<svg
className="w-full h-auto relative -left-px text-blue"
className={classNames("w-full h-auto relative -left-px text-blue")}
xmlns="http://www.w3.org/2000/svg"
width="119"
height="115"
@@ -65,20 +72,25 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
fill="currentColor"
/>
</svg>
<span className="sr-only">Open Jazz Inspector</span>
<span className={classNames("sr-only")}>Open Jazz Inspector</span>
</Button>
);
}
return (
<div className="fixed h-[calc(100%-12rem)] flex flex-col bottom-0 left-0 w-full bg-white border-t border-gray-200 dark:border-stone-900 dark:bg-stone-925">
<div className="flex items-center gap-4 px-3 my-3">
<div
className={classNames(
"fixed h-[calc(100%-12rem)] flex flex-col bottom-0 left-0 w-full bg-red-500 border-t border-gray-200 dark:border-stone-900 dark:bg-stone-925",
)}
id="__jazz_inspector"
>
<div className={classNames("flex items-center gap-4 px-3 my-3")}>
<Breadcrumbs path={path} onBreadcrumbClick={goToIndex} />
<form onSubmit={handleCoValueIdSubmit} className="w-96">
<form onSubmit={handleCoValueIdSubmit} className={classNames("w-96")}>
{path.length !== 0 && (
<Input
label="CoValue ID"
className="font-mono"
className={classNames("font-mono")}
hideLabel
placeholder="co_z1234567890abcdef123456789"
value={coValueId}
@@ -101,14 +113,20 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
<form
onSubmit={handleCoValueIdSubmit}
aria-hidden={path.length !== 0}
className="flex flex-col relative -top-6 justify-center gap-2 h-full w-full max-w-sm mx-auto"
className={classNames(
"flex flex-col relative -top-6 justify-center gap-2 h-full w-full max-w-sm mx-auto",
)}
>
<h2 className="text-lg text-center font-medium mb-4 text-stone-900 dark:text-white">
<h2
className={classNames(
"text-lg text-center font-medium mb-4 text-stone-900 dark:text-white",
)}
>
Jazz CoValue Inspector
</h2>
<Input
label="CoValue ID"
className="min-w-[21rem] font-mono"
className={classNames("min-w-[21rem] font-mono")}
hideLabel
placeholder="co_z1234567890abcdef123456789"
value={coValueId}
@@ -118,7 +136,7 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
Inspect CoValue
</Button>
<p className="text-center">or</p>
<p className={classNames("text-center")}>or</p>
<Button
variant="secondary"

View File

@@ -1,6 +1,7 @@
import { CoID, LocalNode, RawCoValue } from "cojson";
import { Page } from "./page.js"; // Assuming you have a Page component
import { classNames } from "../utils.js";
// Define the structure of a page in the path
interface PageInfo {
coId: CoID<RawCoValue>;
@@ -27,7 +28,11 @@ export function PageStack({
const index = path.length - 1;
return (
<div className="relative px-3 overflow-y-auto flex-1 text-stone-700 dark:text-stone-400">
<div
className={classNames(
"relative px-3 overflow-y-auto flex-1 text-stone-700 dark:text-stone-400",
)}
>
{children}
{node && page && (
<Page

View File

@@ -8,6 +8,8 @@ import { PageInfo } from "./types.js";
import { useResolvedCoValue } from "./use-resolve-covalue.js";
import { AccountOrGroupPreview } from "./value-renderer.js";
import { classNames } from "../utils.js";
type PageProps = {
coId: CoID<RawCoValue>;
node: LocalNode;
@@ -59,7 +61,7 @@ export function Page({
>
{!isTopLevel && (
<div
className="absolute left-0 right-0 top-0 h-10"
className={classNames("absolute left-0 right-0 top-0 h-10")}
aria-label="Back"
onClick={() => {
onHeaderClick?.();
@@ -67,28 +69,40 @@ export function Page({
aria-hidden="true"
></div>
)}
<div className="flex justify-between items-center mb-4">
<div className="flex items-center gap-3">
<h2 className="text-lg font-medium flex flex-col items-start gap-1 text-stone-900 dark:text-white">
<div className={classNames("flex justify-between items-center mb-4")}>
<div className={classNames("flex items-center gap-3")}>
<h2
className={classNames(
"text-lg font-medium flex flex-col items-start gap-1 text-stone-900 dark:text-white",
)}
>
<span>
{name}
{typeof snapshot === "object" && "name" in snapshot ? (
<span className="text-gray-600 font-medium">
<span className={classNames("text-gray-600 font-medium")}>
{" "}
{(snapshot as { name: string }).name}
</span>
) : null}
</span>
</h2>
<span className="text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono">
<span
className={classNames(
"text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono",
)}
>
{type && <TypeIcon type={type} extendedType={extendedType} />}
</span>
<span className="text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono">
<span
className={classNames(
"text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono",
)}
>
{coId}
</span>
</div>
</div>
<div className="overflow-auto">
<div className={classNames("overflow-auto")}>
{type === "costream" ? (
<CoStreamView
data={snapshot}
@@ -102,7 +116,7 @@ export function Page({
<TableView data={snapshot} node={node} onNavigate={onNavigate} />
)}
{extendedType !== "account" && extendedType !== "group" && (
<div className="text-sm text-gray-500 mt-4">
<div className={classNames("text-sm text-gray-500 mt-4")}>
Owned by{" "}
<AccountOrGroupPreview
coId={value.group.id}

View File

@@ -7,6 +7,7 @@ import { PageInfo } from "./types.js";
import { useResolvedCoValues } from "./use-resolve-covalue.js";
import { ValueRenderer } from "./value-renderer.js";
import { classNames } from "../utils.js";
export function TableView({
data,
node,
@@ -52,23 +53,29 @@ export function TableView({
return (
<div>
<table className="min-w-full text-sm border-spacing-0 border-collapse">
<thead className="sticky top-0 border-b border-gray-200">
<table
className={classNames(
"min-w-full text-sm border-spacing-0 border-collapse",
)}
>
<thead className={classNames("sticky top-0 border-b border-gray-200")}>
<tr>
{["", ...keys].map((key) => (
<th
key={key}
className="p-3 bg-gray-50 dark:bg-gray-925 text-left font-medium rounded"
className={classNames(
"p-3 bg-gray-50 dark:bg-gray-925 text-left font-medium rounded",
)}
>
{key}
</th>
))}
</tr>
</thead>
<tbody className=" border-t border-gray-200">
<tbody className={classNames(" border-t border-gray-200")}>
{resolvedRows.slice(0, visibleRowsCount).map((item, index) => (
<tr key={index}>
<td className="p-1">
<td className={classNames("p-1")}>
<Button
variant="tertiary"
onClick={() =>
@@ -84,7 +91,7 @@ export function TableView({
</Button>
</td>
{keys.map((key) => (
<td key={key} className="p-4 whitespace-nowrap">
<td key={key} className={classNames("p-4 whitespace-nowrap")}>
<ValueRenderer
json={(item.snapshot as JsonObject)[key]}
onCoIDClick={(coId) => {
@@ -110,17 +117,23 @@ export function TableView({
))}
</tbody>
</table>
<div className="py-4 text-gray-500 flex items-center justify-between gap-2">
<div
className={classNames(
"py-4 text-gray-500 flex items-center justify-between gap-2",
)}
>
<span>
Showing {Math.min(visibleRowsCount, coIdArray.length)} of{" "}
{coIdArray.length}
</span>
{hasMore && (
<div className="text-center">
<div className={classNames("text-center")}>
<Button
variant="plain"
onClick={loadMore}
className="px-4 py-2 bg-blue text-white rounded hover:bg-blue-800"
className={classNames(
"px-4 py-2 bg-blue text-white rounded hover:bg-blue-800",
)}
>
Load more
</Button>

View File

@@ -5,6 +5,8 @@ import {
useResolvedCoValue,
} from "./use-resolve-covalue.js";
import { classNames } from "../utils.js";
export const TypeIcon = ({
type,
extendedType,
@@ -25,7 +27,7 @@ export const TypeIcon = ({
const iconKey = extendedType || type;
const icon = iconMap[iconKey as keyof typeof iconMap];
return icon ? <span className="font-mono">{icon}</span> : null;
return icon ? <span className={classNames("font-mono")}>{icon}</span> : null;
};
export const ResolveIcon = ({
@@ -38,10 +40,13 @@ export const ResolveIcon = ({
const { type, extendedType, snapshot } = useResolvedCoValue(coId, node);
if (snapshot === "unavailable" && !type) {
return <div className="text-gray-600 font-medium">Unavailable</div>;
return (
<div className={classNames("text-gray-600 font-medium")}>Unavailable</div>
);
}
if (!type) return <div className="whitespace-pre w-14 font-mono"> </div>;
if (!type)
return <div className={classNames("whitespace-pre w-14 font-mono")}> </div>;
return <TypeIcon type={type} extendedType={extendedType} />;
};

View File

@@ -1,8 +1,8 @@
import clsx from "clsx";
import { CoID, JsonValue, LocalNode, RawCoValue } from "cojson";
import React, { useEffect, useState } from "react";
import { LinkIcon } from "../link-icon.js";
import { Button } from "../ui/button.js";
import { classNames } from "../utils.js";
import {
isBrowserImage,
resolveCoValue,
@@ -20,11 +20,11 @@ export function ValueRenderer({
const [isExpanded, setIsExpanded] = useState(false);
if (typeof json === "undefined" || json === undefined) {
return <span className="text-gray-400">undefined</span>;
return <span className={classNames("text-gray-400")}>undefined</span>;
}
if (json === null) {
return <span className="text-gray-400">null</span>;
return <span className={classNames("text-gray-400")}>null</span>;
}
if (typeof json === "string" && json.startsWith("co_")) {
@@ -42,7 +42,7 @@ export function ValueRenderer({
if (onCoIDClick) {
return (
<Button
className={linkClasses}
className={classNames(linkClasses)}
onClick={() => {
onCoIDClick?.(json as CoID<RawCoValue>);
}}
@@ -53,27 +53,31 @@ export function ValueRenderer({
);
}
return <span className={linkClasses}>{content}</span>;
return <span className={classNames(linkClasses)}>{content}</span>;
}
if (typeof json === "string") {
return (
<span className="text-green-900 font-mono">
{/* <span className="select-none opacity-70">{'"'}</span> */}
<span
className={classNames("text-green-700 font-mono dark:text-green-400")}
>
{json}
{/* <span className="select-none opacity-70">{'"'}</span> */}
</span>
);
}
if (typeof json === "number") {
return <span className="text-purple-500">{json}</span>;
return (
<span className={classNames("text-purple-700 dark:text-purple-400")}>
{json}
</span>
);
}
if (typeof json === "boolean") {
return (
<span
className={clsx(
className={classNames(
json
? "text-green-700 bg-green-700/5"
: "text-amber-700 bg-amber-500/5",
@@ -90,12 +94,12 @@ export function ValueRenderer({
return (
<span
title={JSON.stringify(json, null, 2)}
className="inline-block max-w-64"
className={classNames("inline-block max-w-64")}
>
<span className="text-gray-600">
<span className={classNames("text-gray-600")}>
{Array.isArray(json) ? <>Array ({json.length})</> : <>Object</>}
</span>
<pre className="mt-1.5 text-sm whitespace-pre-wrap">
<pre className={classNames("mt-1.5 text-sm whitespace-pre-wrap")}>
{isExpanded
? JSON.stringify(json, null, 2)
: JSON.stringify(json, null, 2).split("\n").slice(0, 3).join("\n") +
@@ -104,7 +108,9 @@ export function ValueRenderer({
<Button
variant="plain"
onClick={() => setIsExpanded(!isExpanded)}
className="mt-1.5 text-sm text-gray-600 hover:text-gray-700"
className={classNames(
"mt-1.5 text-sm text-gray-600 hover:text-gray-700",
)}
>
{isExpanded ? "Show less" : "Show more"}
</Button>
@@ -131,14 +137,18 @@ export const CoMapPreview = ({
if (!snapshot) {
return (
<div className="rounded bg-gray-100 animate-pulse whitespace-pre w-24">
<div
className={classNames(
"rounded bg-gray-100 animate-pulse whitespace-pre w-24",
)}
>
{" "}
</div>
);
}
if (snapshot === "unavailable" && !value) {
return <div className="text-gray-500">Unavailable</div>;
return <div className={classNames("text-gray-500")}>Unavailable</div>;
}
if (extendedType === "image" && isBrowserImage(snapshot)) {
@@ -146,9 +156,11 @@ export const CoMapPreview = ({
<div>
<img
src={snapshot.placeholderDataURL}
className="size-8 border-2 border-white drop-shadow-md my-2"
className={classNames(
"size-8 border-2 border-white drop-shadow-md my-2",
)}
/>
<span className="text-gray-500 text-sm">
<span className={classNames("text-gray-500 text-sm")}>
{snapshot.originalSize[0]} x {snapshot.originalSize[1]}
</span>
@@ -164,7 +176,9 @@ export const CoMapPreview = ({
return (
<div>
Record{" "}
<span className="text-gray-500">({Object.keys(snapshot).length})</span>
<span className={classNames("text-gray-500")}>
({Object.keys(snapshot).length})
</span>
</div>
);
}
@@ -173,7 +187,7 @@ export const CoMapPreview = ({
return (
<div>
List{" "}
<span className="text-gray-500">
<span className={classNames("text-gray-500")}>
({(snapshot as unknown as []).length})
</span>
</div>
@@ -181,13 +195,13 @@ export const CoMapPreview = ({
}
return (
<div className="text-sm flex flex-col gap-2 items-start">
<div className="grid grid-cols-[auto_1fr] gap-2">
<div className={classNames("text-sm flex flex-col gap-2 items-start")}>
<div className={classNames("grid grid-cols-[auto_1fr] gap-2")}>
{Object.entries(snapshot)
.slice(0, limit)
.map(([key, value]) => (
<React.Fragment key={key}>
<span className="font-medium">{key}: </span>
<span className={classNames("font-medium")}>{key}: </span>
<span>
<ValueRenderer json={value} />
</span>
@@ -195,7 +209,7 @@ export const CoMapPreview = ({
))}
</div>
{Object.entries(snapshot).length > limit && (
<div className="text-left text-sm text-gray-500 mt-2">
<div className={classNames("text-left text-sm text-gray-500 mt-2")}>
{Object.entries(snapshot).length - limit} more
</div>
)}
@@ -245,10 +259,10 @@ export function AccountOrGroupPreview({
const props = onClick
? {
onClick: () => onClick(displayName),
className: "text-blue-500 cursor-pointer hover:underline",
className: classNames("text-blue-500 cursor-pointer hover:underline"),
}
: {
className: "text-gray-500",
className: classNames("text-gray-500"),
};
return <span {...props}>{displayText}</span>;