mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
The magnifying glass was drawn over the leading "S" of the placeholder. The cause is not the search bar: @luxfi/ui's InputGroup measures its adornments correctly and then throws the measurement away, because the clone is guarded on `displayName === 'FieldInput'` — a component that does not exist in the package — and the props it would have set (`ps`/`pe`) are Chakra style props that the Tailwind/gui Input spreads onto the DOM and ignores. Both halves are leftovers of the Chakra→Tailwind port, so EVERY InputGroup in the app is unpadded; the header is just the one you look at every day. Patched at the primitive (patches/@[email protected]) rather than hand-tuning padding at one call site: the offset is the measured width of the actual adornment, so it stays right when the adornment changes, and FilterInput / revoke's SearchInput / the showcase are fixed by the same change. Measured at 1440x900: the icon overlapped the text by 24px and the kbd chip by 52px; both are now 0. The measurement also moves to a layout effect and stops blanking value and placeholder on first render. That blanking was inert while the guard swallowed the clone; with the clone live it flips the field from controlled to uncontrolled on mount, which React warns about and which can eat a keystroke. ClearButton spread `className` over its own visibility classes, so a caller passing one clobbered `invisible` — the header showed a clear button on an empty field. Merged instead, at the component. Placeholder is now "Search or ask anything", and Cmd/Ctrl-K opens an assistant. Nothing in this repo, @luxfi/ui or @hanzo/gui ships a command palette or chat (no cmdk, no palette, one dialog primitive), so this is the smallest thing that works: the existing Dialog, one global listener, and a server-side proxy that holds the credential. The key is deliberately not NEXT_PUBLIC_, so a deployment without one gets a dialog that says exactly that instead of failing silently. Off unless NEXT_PUBLIC_AI_ASSISTANT_ENABLED is set — Ctrl+K is the browser's address-bar shortcut and claiming it to show an error would be worse than not claiming it. Validators read 5 on a network running 20. useCurrentValidators asks ONE P-Chain, the one this deployment points at, but Lux, Hanzo, Zoo and Pars are each sovereign L1s with their own P-Chain and their own validator set — measured 2026-08-06, five each, with four disjoint NodeID sets. The registry gains the node origin per chain; absence of it is the signal that a chain has no publicly reachable node (SPC and Osage are in-cluster only), and those are reported `unavailable` rather than counted as zero. Stake is deliberately NOT summed: each chain bonds its own currency. The proxy resolves `?chain=` through the registry and never from the request, so it cannot be pointed at an arbitrary origin. The new envs are registered with the envs-validator and documented in ENVS.md — the schema rejects unknown NEXT_PUBLIC_* variables and runs at container start, so an undeclared one stops the pod from booting. Co-authored-by: Hanzo Dev <[email protected]>
164 lines
8.0 KiB
Diff
164 lines
8.0 KiB
Diff
diff --git a/dist/input-group.cjs b/dist/input-group.cjs
|
|
index 08c4f4567a919bf51f2d75e67591c4e0285809e9..fc3e318a037600182fec9c3829a5e09a9a79e134 100644
|
|
--- a/dist/input-group.cjs
|
|
+++ b/dist/input-group.cjs
|
|
@@ -31,11 +31,7 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
function cn(...inputs) {
|
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
}
|
|
-function getComponentDisplayName(type) {
|
|
- if (typeof type === "string") return void 0;
|
|
- if ("displayName" in type) return type.displayName;
|
|
- return void 0;
|
|
-}
|
|
+const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React__namespace.useLayoutEffect : React__namespace.useEffect;
|
|
var InputGroup = React__namespace.forwardRef(
|
|
function InputGroup2(props, ref) {
|
|
const {
|
|
@@ -81,7 +77,7 @@ var InputGroup = React__namespace.forwardRef(
|
|
}
|
|
};
|
|
}, [calculateInlinePaddings]);
|
|
- React__namespace.useEffect(() => {
|
|
+ useIsomorphicLayoutEffect(() => {
|
|
calculateInlinePaddings();
|
|
const resizeHandler = esToolkit.debounce(calculateInlinePaddings, 300);
|
|
const resizeObserver = new ResizeObserver(resizeHandler);
|
|
@@ -122,15 +118,18 @@ var InputGroup = React__namespace.forwardRef(
|
|
}
|
|
),
|
|
React__namespace.Children.map(children, (child) => {
|
|
- if (getComponentDisplayName(child.type) !== "FieldInput") {
|
|
+ if (!React__namespace.isValidElement(child)) {
|
|
return child;
|
|
}
|
|
+ const start = startElement ? startOffset ?? inlinePaddings?.start : void 0;
|
|
+ const end = endElement ? endOffset ?? inlinePaddings?.end : void 0;
|
|
+ const px = (v) => typeof v === "number" ? `${v}px` : v;
|
|
return React__namespace.cloneElement(child, {
|
|
- ...startElement && { ps: startOffset ?? (inlinePaddings?.start ? `${inlinePaddings.start}px` : void 0) },
|
|
- ...endElement && { pe: endOffset ?? (inlinePaddings?.end ? `${inlinePaddings.end}px` : void 0) },
|
|
- // hide input value and placeholder for the first render
|
|
- value: inlinePaddings ? child.props.value : void 0,
|
|
- placeholder: inlinePaddings ? child.props.placeholder : void 0
|
|
+ style: {
|
|
+ ...child.props.style,
|
|
+ ...start !== void 0 && { paddingInlineStart: px(start) },
|
|
+ ...end !== void 0 && { paddingInlineEnd: px(end) }
|
|
+ }
|
|
});
|
|
}),
|
|
endElement && /* @__PURE__ */ jsxRuntime.jsx(
|
|
diff --git a/dist/input-group.js b/dist/input-group.js
|
|
index 71035e813aced180b16bf665d88e7cdbbaa79f90..d56379695166d6b53a12c0b68a255049e0759a63 100644
|
|
--- a/dist/input-group.js
|
|
+++ b/dist/input-group.js
|
|
@@ -9,11 +9,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
function cn(...inputs) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
-function getComponentDisplayName(type) {
|
|
- if (typeof type === "string") return void 0;
|
|
- if ("displayName" in type) return type.displayName;
|
|
- return void 0;
|
|
-}
|
|
+const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
|
|
var InputGroup = React.forwardRef(
|
|
function InputGroup2(props, ref) {
|
|
const {
|
|
@@ -59,7 +55,7 @@ var InputGroup = React.forwardRef(
|
|
}
|
|
};
|
|
}, [calculateInlinePaddings]);
|
|
- React.useEffect(() => {
|
|
+ useIsomorphicLayoutEffect(() => {
|
|
calculateInlinePaddings();
|
|
const resizeHandler = debounce(calculateInlinePaddings, 300);
|
|
const resizeObserver = new ResizeObserver(resizeHandler);
|
|
@@ -100,15 +96,18 @@ var InputGroup = React.forwardRef(
|
|
}
|
|
),
|
|
React.Children.map(children, (child) => {
|
|
- if (getComponentDisplayName(child.type) !== "FieldInput") {
|
|
+ if (!React.isValidElement(child)) {
|
|
return child;
|
|
}
|
|
+ const start = startElement ? startOffset ?? inlinePaddings?.start : void 0;
|
|
+ const end = endElement ? endOffset ?? inlinePaddings?.end : void 0;
|
|
+ const px = (v) => typeof v === "number" ? `${v}px` : v;
|
|
return React.cloneElement(child, {
|
|
- ...startElement && { ps: startOffset ?? (inlinePaddings?.start ? `${inlinePaddings.start}px` : void 0) },
|
|
- ...endElement && { pe: endOffset ?? (inlinePaddings?.end ? `${inlinePaddings.end}px` : void 0) },
|
|
- // hide input value and placeholder for the first render
|
|
- value: inlinePaddings ? child.props.value : void 0,
|
|
- placeholder: inlinePaddings ? child.props.placeholder : void 0
|
|
+ style: {
|
|
+ ...child.props.style,
|
|
+ ...start !== void 0 && { paddingInlineStart: px(start) },
|
|
+ ...end !== void 0 && { paddingInlineEnd: px(end) }
|
|
+ }
|
|
});
|
|
}),
|
|
endElement && /* @__PURE__ */ jsx(
|
|
diff --git a/src/input-group.tsx b/src/input-group.tsx
|
|
index fc0ed50dad745ae18315571bd44367a5eb86faab..120a95ecfb9c05dba23619288896bf095a2115f8 100644
|
|
--- a/src/input-group.tsx
|
|
+++ b/src/input-group.tsx
|
|
@@ -5,11 +5,11 @@ import { cn } from './utils';
|
|
|
|
import type { InputProps } from './input';
|
|
|
|
-function getComponentDisplayName(type: string | React.JSXElementConstructor<unknown>): string | undefined {
|
|
- if (typeof type === 'string') return undefined;
|
|
- if ('displayName' in type) return type.displayName as string;
|
|
- return undefined;
|
|
-}
|
|
+// The adornment widths must be applied before the browser paints, or the field
|
|
+// renders one frame with default padding and the text visibly jumps out from
|
|
+// under the icon. useLayoutEffect does that but has no meaning while rendering
|
|
+// on the server, where it only warns.
|
|
+const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
|
|
export interface InputElementProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
readonly placement?: 'start' | 'end';
|
|
@@ -81,7 +81,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
};
|
|
}, [ calculateInlinePaddings ]);
|
|
|
|
- React.useEffect(() => {
|
|
+ useIsomorphicLayoutEffect(() => {
|
|
calculateInlinePaddings();
|
|
|
|
const resizeHandler = debounce(calculateInlinePaddings, 300);
|
|
@@ -128,15 +128,24 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
</div>
|
|
) }
|
|
{ React.Children.map(children, (child: React.ReactElement<InputProps>) => {
|
|
- if (getComponentDisplayName(child.type) !== 'FieldInput') {
|
|
+ if (!React.isValidElement(child)) {
|
|
return child;
|
|
}
|
|
+ // The adornments are absolutely positioned over the field, so the
|
|
+ // control has to be inset by their measured width or the text starts
|
|
+ // underneath them. Written as inline padding rather than the `ps`/`pe`
|
|
+ // style props this once carried: those are Chakra's, and Input is now
|
|
+ // a Tailwind/gui component that spreads unknown props onto the DOM,
|
|
+ // so they applied nothing and the icon sat on the placeholder.
|
|
+ const start = startElement ? startOffset ?? inlinePaddings?.start : undefined;
|
|
+ const end = endElement ? endOffset ?? inlinePaddings?.end : undefined;
|
|
+ const px = (v: string | number) => typeof v === 'number' ? `${ v }px` : v;
|
|
return React.cloneElement(child, {
|
|
- ...(startElement && { ps: startOffset ?? (inlinePaddings?.start ? `${ inlinePaddings.start }px` : undefined) }),
|
|
- ...(endElement && { pe: endOffset ?? (inlinePaddings?.end ? `${ inlinePaddings.end }px` : undefined) }),
|
|
- // hide input value and placeholder for the first render
|
|
- value: inlinePaddings ? child.props.value : undefined,
|
|
- placeholder: inlinePaddings ? child.props.placeholder : undefined,
|
|
+ style: {
|
|
+ ...child.props.style,
|
|
+ ...(start !== undefined && { paddingInlineStart: px(start) }),
|
|
+ ...(end !== undefined && { paddingInlineEnd: px(end) }),
|
|
+ },
|
|
});
|
|
}) }
|
|
{ endElement && (
|