Skip to content

Commit 21b1acc

Browse files
github-actions[bot]bsiaotickchongJasonCWangcoreymartinpengying
authored
Updates to ui, lightspark-sdk (#475)
* [nage] add details panel component, add table view for api tokens, add TextInput and DropdownInput components (#19644) - adds table for api tokens w temporary data - adds detail panel component that opens when clicking add token - uses react context to handle button in the tab bar opening the api token panel ![Screenshot 2025-08-06 at 2.20.08 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/NU8OmLauzLqa61yWDJkY/3f98da2a-9fb2-44ac-90a6-0c51fecfb9d8.png) ![Screenshot 2025-08-06 at 2.20.49 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/NU8OmLauzLqa61yWDJkY/086f6ae1-6edb-49ae-a593-9cc61243072a.png) GitOrigin-RevId: fd8413408c5007db784d07a962b1f565fc35b8a0 * [Nage] Add skeleton control center (#19499) ![Screenshot 2025-08-06 at 5.16.56 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/EJphitdgYFvuACNazm1q/494ef388-6fcd-4a67-82ca-0bd1da2ce54d.png) ![Screenshot 2025-08-06 at 5.16.59 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/EJphitdgYFvuACNazm1q/e12edd93-04b7-4c24-a1b4-12978a18e0cc.png) GitOrigin-RevId: abddd8eb5c7446b43d692167f5df22b5d58ca51d * [nage] add zustand state for nage, add dropdown component (#19720) - adds state to site global store for nage - adds Dropdown component styled for nage - todo: add queries for platforms (hardcoded sandbox and prod) ![Screenshot 2025-08-08 at 1.15.05 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/NU8OmLauzLqa61yWDJkY/69ffdcbb-8af2-4038-bd78-bafd6586e179.png) ![Screenshot 2025-08-08 at 1.15.37 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/NU8OmLauzLqa61yWDJkY/da0bc733-8a6f-49b4-b079-e163a0c83e04.png) GitOrigin-RevId: 54f55115dc537f3411e63630e5829453addd6798 * Add preliminary UI for account configuration (#19697) ![Screenshot 2025-08-11 at 5.08.47 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/EJphitdgYFvuACNazm1q/babbadf6-85d7-4299-ae3e-d9b4c8805b02.png) GitOrigin-RevId: 68f907519ef956a8935c7ff2304910885921c073 * [lightspark-sdk] Handle double-encoded variables from WASM in RemoteSigningWebhookHandler (#19946) ## Reason @pengying discovered that apps/remote-signing-server is failing due to variables being sent to sparkcore as a string instead of a plain object. We discovered this is because the variables returned from a WASM call is double-encoded JSON. The code is not widely used which explains why we haven't noticed until now. ## Overview For large or complex changes, describe what is being changed. ## Test Plan Tested manually and ensure no existing tests are broken. GitOrigin-RevId: 347cb22a77fcc6f61fc3cffdd879078ac9d4982c * chore: adding changesets for sdk --------- Co-authored-by: Brian Siao Tick Chong <bsiaotickchong@gmail.com> Co-authored-by: Jason Wang <jason@lightspark.com> Co-authored-by: Corey Martin <coreyn.martin@gmail.com> Co-authored-by: Peng Ying <peng@lightspark.com>
1 parent 6a221ad commit 21b1acc

14 files changed

Lines changed: 163 additions & 18 deletions

File tree

.changeset/hot-bags-rescue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@lightsparkdev/lightspark-sdk": patch
3+
---
4+
5+
fix: wasm returned a double stringified json object where the webhook signing handler only expected it to be stringifed once. This change parses the string twice.

.changeset/slimy-walls-make.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@lightsparkdev/ui": patch
3+
---
4+
5+
feat: New UI elements for Lightspark FE

packages/lightspark-sdk/src/webhooks.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,26 @@ export class RemoteSigningWebhookHandler {
8484
if (!response) {
8585
return;
8686
}
87-
const variables = JSON.parse(response.variables);
87+
88+
/* WASM returns a double-encoded JSON string for variables: */
89+
let jsonVariablesString: string;
90+
let variables: {
91+
[key: string]: unknown;
92+
};
93+
try {
94+
jsonVariablesString = JSON.parse(response.variables);
95+
} catch (e) {
96+
throw new LightsparkSigningException(
97+
"Unable to get JSON variables string from response",
98+
);
99+
}
100+
try {
101+
variables = JSON.parse(jsonVariablesString);
102+
} catch (e) {
103+
throw new LightsparkSigningException(
104+
"Unable to parse JSON variables from response",
105+
);
106+
}
88107
return this.client.executeRawQuery({
89108
queryPayload: response.query,
90109
variables,

packages/ui/src/components/Button.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export type ButtonProps = {
9999
ml?: number | "auto";
100100
mb?: number | "auto";
101101
fullWidth?: boolean | undefined;
102+
spaceBetween?: boolean | undefined;
102103
type?: "button" | "submit";
103104
newTab?: boolean;
104105
tooltipText?: string | undefined;
@@ -272,6 +273,7 @@ export const Button = forwardRef<
272273
loading = false,
273274
loadingKind = "primary",
274275
fullWidth = false,
276+
spaceBetween = false,
275277
disabled = false,
276278
mt = 0,
277279
ml = 0,
@@ -325,7 +327,7 @@ export const Button = forwardRef<
325327
css={{
326328
display: "flex",
327329
alignItems: "center",
328-
justifyContent: "center",
330+
justifyContent: spaceBetween ? "space-between" : "center",
329331
gap: "8px",
330332
}}
331333
>

packages/ui/src/components/DataManagerTable/DataManagerTable.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,10 @@ const PageNumberButton = styled.button<{ isCurrentPage: boolean }>`
10561056
border: none;
10571057
background-color: transparent;
10581058
&:hover {
1059-
border: 0.5px solid ${colors["black-10"]};
1059+
background-color: ${colors["black-04"]};
1060+
}
1061+
&:active {
1062+
background-color: ${colors["black-10"]};
10601063
}
10611064
${({ isCurrentPage }) =>
10621065
isCurrentPage

packages/ui/src/components/Dropdown.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ type DropdownItemType = {
7272

7373
type DropdownGetProps = WithTheme<{ isOpen: boolean }>;
7474

75-
type DropdownBorderRadius = 8 | 12;
75+
type DropdownBorderRadius = 6 | 8 | 12;
7676

7777
type CommonSimpleDropdownButton = {
7878
id?: string;
7979
getCSS?: ({ isOpen, theme }: DropdownGetProps) => CSSInterpolation;
8080
};
8181

82-
type SimpleDropdownButton =
82+
export type SimpleDropdownButton =
8383
| (CommonSimpleDropdownButton & {
8484
label: string;
8585
})
@@ -100,6 +100,7 @@ type DropdownProps = {
100100
verticalPlacement?: "top" | "bottom";
101101
footer?: ReactNode | null;
102102
getCSS?: ({ isOpen, theme }: DropdownGetProps) => CSSObject;
103+
getDropdownItemsCSS?: ({ isOpen, theme }: DropdownGetProps) => CSSObject;
103104
onOpen?: () => void;
104105
onClose?: () => void;
105106
isOpen?: boolean;
@@ -113,6 +114,7 @@ export function Dropdown({
113114
horizontalScrollRef,
114115
onClickDropdownItems,
115116
getCSS,
117+
getDropdownItemsCSS,
116118
isOpen: isOpenProp,
117119
onOpen,
118120
onClose,
@@ -368,6 +370,8 @@ export function Dropdown({
368370
}
369371

370372
const containerCSS = getCSS && getCSS({ isOpen, theme });
373+
const dropdownItemsCSS =
374+
getDropdownItemsCSS && getDropdownItemsCSS({ isOpen, theme });
371375

372376
return (
373377
<div
@@ -386,6 +390,7 @@ export function Dropdown({
386390
maxWidth={maxDropdownItemsWidth}
387391
hasItems={dropdownItems ? dropdownItems.length > 0 : false}
388392
borderRadius={borderRadius}
393+
css={dropdownItemsCSS}
389394
>
390395
{dropdownItems?.map((dropdownItem, i) => {
391396
return (

packages/ui/src/components/Modal.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ type ModalProps = {
121121
drawerCloseButton?: boolean;
122122
drawerAlignBottom?: boolean | undefined;
123123
drawerDisableTouchMove?: boolean | undefined;
124+
voidOverlayBackground?: boolean | undefined;
124125
};
125126

126127
export function Modal({
@@ -161,6 +162,7 @@ export function Modal({
161162
drawerCloseButton = true,
162163
drawerAlignBottom,
163164
drawerDisableTouchMove,
165+
voidOverlayBackground,
164166
}: ModalProps) {
165167
const visibleChangedRef = useRef(false);
166168
const [visibleChanged, setVisibleChanged] = useState(false);
@@ -428,7 +430,10 @@ export function Modal({
428430
content = (
429431
<Fragment>
430432
{!(smKind === "fullscreen" && bp.isSm()) ? (
431-
<ModalOverlay ref={overlayRef} />
433+
<ModalOverlay
434+
ref={overlayRef}
435+
voidOverlayBackground={voidOverlayBackground ?? false}
436+
/>
432437
) : null}
433438
<ModalContainer
434439
aria-modal
@@ -486,14 +491,15 @@ const DefaultFocusTarget = styled(UnstyledButton)`
486491
height: 0;
487492
`;
488493

489-
const ModalOverlay = styled.div`
494+
const ModalOverlay = styled.div<{ voidOverlayBackground?: boolean }>`
490495
position: fixed;
491496
bottom: 0;
492497
left: 0;
493498
z-index: ${z.modalOverlay};
494499
width: 100vw;
495500
height: 100vh;
496-
background: rgba(0, 0, 0, 0.5);
501+
background: ${({ voidOverlayBackground }) =>
502+
voidOverlayBackground ? "transparent" : "rgba(0, 0, 0, 0.5)"};
497503
`;
498504

499505
const ModalContainer = styled.div<{ top: number }>`

packages/ui/src/components/TextInput.tsx

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,23 @@ import {
2323
type TextInputBorderRadius,
2424
} from "../styles/fields.js";
2525
import {
26+
getBackgroundColor,
2627
getFontColor,
2728
type FontColorKey,
2829
type ThemeOrColorKey,
2930
} from "../styles/themes.js";
3031
import { applyTypography } from "../styles/typography.js";
3132
import { z } from "../styles/z-index.js";
3233
import type { ToReactNodesArgs } from "../utils/toReactNodes/toReactNodes.js";
34+
import { toReactNodes } from "../utils/toReactNodes/toReactNodes.js";
3335
import { CheckboxContainer } from "./Checkbox.js";
3436
import { Icon, IconContainer } from "./Icon/Icon.js";
3537
import { type IconName } from "./Icon/types.js";
3638
import { Loading } from "./Loading.js";
3739
import { ToggleContainer } from "./Toggle.js";
3840
import { Tooltip } from "./Tooltip.js";
3941
import { UnstyledButton } from "./UnstyledButton.js";
42+
import { type TypographyPropsWithoutChildren } from "./typography/renderTypography.js";
4043
import {
4144
type PartialSimpleTypographyProps,
4245
type RequiredSimpleTypographyProps,
@@ -53,6 +56,16 @@ export type IconWidth = (typeof iconWidths)[number];
5356
export const iconStrokeWidths = [1, 1.5, 2] as const;
5457
export type IconStrokeWidth = (typeof iconStrokeWidths)[number];
5558

59+
export type TextLabelOptions = {
60+
backgroundColor?: ThemeOrColorKey;
61+
borderRadius?: TextInputBorderRadius;
62+
paddingX?: number;
63+
paddingY?: number;
64+
typography?: TypographyPropsWithoutChildren;
65+
position?: "absolute" | "relative";
66+
marginBottom?: number;
67+
};
68+
5669
export type TextInputProps = {
5770
disabled?: boolean | undefined;
5871
error?: string | ToReactNodesArgs | undefined;
@@ -101,6 +114,7 @@ export type TextInputProps = {
101114
hideNonErrorsIfBlurred?: boolean | undefined;
102115
hintTooltip?: string | undefined;
103116
label?: string;
117+
labelOptions?: TextLabelOptions;
104118
rightButtonText?: string | undefined;
105119
onRightButtonClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
106120
typography?: PartialSimpleTypographyProps | undefined;
@@ -342,7 +356,14 @@ export function TextInput(textInputProps: TextInputProps) {
342356
return (
343357
<StyledTextInput widthProp={textInputWidth} marginTop={props.marginTop}>
344358
{props.label ? (
345-
<TextInputLabel hasError={hasError}>{props.label}</TextInputLabel>
359+
<TextInputLabel hasError={hasError} labelOptions={props.labelOptions}>
360+
{props.labelOptions?.typography
361+
? toReactNodes({
362+
text: props.label,
363+
typography: props.labelOptions.typography,
364+
})
365+
: props.label}
366+
</TextInputLabel>
346367
) : null}
347368
{select && (
348369
<TextInputSelect
@@ -486,18 +507,46 @@ const RightButton = styled(UnstyledButton)`
486507
padding: 8px 10px;
487508
`;
488509

489-
const TextInputLabel = styled.label<{ hasError: boolean }>`
490-
${standardBorderRadius(8)}
510+
const TextInputLabel = styled.label<{
511+
hasError: boolean;
512+
labelOptions?: TextLabelOptions | undefined;
513+
}>`
514+
${({ labelOptions }) =>
515+
labelOptions?.borderRadius
516+
? `${labelOptions.borderRadius}px`
517+
: standardBorderRadius(8)};
491518
font-size: 10px;
492-
position: absolute;
519+
position: ${({ labelOptions }) => labelOptions?.position || "absolute"};
493520
z-index: ${z.textInput + 1};
494-
background-color: ${({ theme }) => theme.bg};
521+
background-color: ${({ theme, labelOptions }) =>
522+
labelOptions?.backgroundColor
523+
? getBackgroundColor(theme, labelOptions.backgroundColor)
524+
: theme.bg};
495525
color: ${({ theme, hasError }) =>
496526
hasError ? theme.danger : theme.mcNeutral};
497527
font-weight: 600;
498-
padding: 4px 6px;
499-
left: 12px;
500-
top: -10px;
528+
padding-left: ${({ labelOptions }) =>
529+
labelOptions?.paddingX === undefined
530+
? "6px"
531+
: `${labelOptions.paddingX}px`};
532+
padding-right: ${({ labelOptions }) =>
533+
labelOptions?.paddingX === undefined
534+
? "6px"
535+
: `${labelOptions.paddingX}px`};
536+
padding-top: ${({ labelOptions }) =>
537+
labelOptions?.paddingY === undefined
538+
? "4px"
539+
: `${labelOptions.paddingY}px`};
540+
padding-bottom: ${({ labelOptions }) =>
541+
labelOptions?.paddingY === undefined
542+
? "4px"
543+
: `${labelOptions.paddingY}px`};
544+
left: ${({ labelOptions }) =>
545+
labelOptions?.position === "relative" ? "0" : "12px"};
546+
top: ${({ labelOptions }) =>
547+
labelOptions?.position === "relative" ? "0" : "-10px"};
548+
margin-bottom: ${({ labelOptions }) =>
549+
labelOptions?.marginBottom ? `${labelOptions.marginBottom}px` : "0"};
501550
`;
502551

503552
export const TextInputHalfRow = styled.div`
@@ -514,6 +563,8 @@ const StyledTextInput = styled.div<{
514563
widthProp: string;
515564
marginTop: number | undefined;
516565
}>`
566+
display: flex;
567+
flex-direction: column;
517568
width: ${({ widthProp }) => widthProp};
518569
position: relative;
519570
/* Apply marginTop to every TextInput when specified */
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { type PathProps } from "../types.js";
2+
3+
export function Brackets1({
4+
strokeWidth = "2",
5+
strokeLinecap = "round",
6+
strokeLinejoin = "round",
7+
}: PathProps) {
8+
return (
9+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
10+
<path
11+
d="M7.25 3.75H6.75C5.09315 3.75 3.75 5.09315 3.75 6.75V17.25C3.75 18.9069 5.09315 20.25 6.75 20.25H7.25M16.75 3.75H17.25C18.9069 3.75 20.25 5.09315 20.25 6.75V17.25C20.25 18.9069 18.9069 20.25 17.25 20.25H16.75"
12+
stroke="currentColor"
13+
strokeWidth={strokeWidth}
14+
strokeLinecap={strokeLinecap}
15+
strokeLinejoin={strokeLinejoin}
16+
/>
17+
</svg>
18+
);
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { type PathProps } from "../types.js";
2+
3+
export function Moon({
4+
strokeWidth = "2",
5+
strokeLinecap = "round",
6+
strokeLinejoin = "round",
7+
}: PathProps) {
8+
return (
9+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
10+
<path
11+
d="M21.2481 11.8112C20.1889 12.56 18.8958 13 17.5 13C13.9101 13 11 10.0899 11 6.5C11 5.10416 11.44 3.81108 12.1888 2.75189C12.126 2.75063 12.0631 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 11.9369 21.2494 11.874 21.2481 11.8112Z"
12+
stroke="currentColor"
13+
strokeWidth={strokeWidth}
14+
strokeLinecap={strokeLinecap}
15+
strokeLinejoin={strokeLinejoin}
16+
/>
17+
</svg>
18+
);
19+
}

0 commit comments

Comments
 (0)