@flanksource/clicky-ui 0.3.57 → 0.3.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/clicky.cjs +3 -1
  2. package/dist/clicky.d.ts +1 -0
  3. package/dist/clicky.d.ts.map +1 -1
  4. package/dist/clicky.js +3 -1
  5. package/dist/data/Clicky.cjs +22 -270
  6. package/dist/data/Clicky.cjs.map +1 -1
  7. package/dist/data/Clicky.d.ts +3 -29
  8. package/dist/data/Clicky.d.ts.map +1 -1
  9. package/dist/data/Clicky.js +10 -259
  10. package/dist/data/Clicky.js.map +1 -1
  11. package/dist/data/clicky-download.cjs +251 -0
  12. package/dist/data/clicky-download.cjs.map +1 -0
  13. package/dist/data/clicky-download.d.ts +55 -0
  14. package/dist/data/clicky-download.d.ts.map +1 -0
  15. package/dist/data/clicky-download.js +244 -0
  16. package/dist/data/clicky-download.js.map +1 -0
  17. package/dist/data/traces/index.d.ts +2 -0
  18. package/dist/data/traces/index.d.ts.map +1 -1
  19. package/dist/data/traces/otel/OtelTraceDetail.cjs +102 -78
  20. package/dist/data/traces/otel/OtelTraceDetail.cjs.map +1 -1
  21. package/dist/data/traces/otel/OtelTraceDetail.d.ts +6 -2
  22. package/dist/data/traces/otel/OtelTraceDetail.d.ts.map +1 -1
  23. package/dist/data/traces/otel/OtelTraceDetail.js +102 -78
  24. package/dist/data/traces/otel/OtelTraceDetail.js.map +1 -1
  25. package/dist/data/traces/otel/OtelTraceTable.cjs +43 -7
  26. package/dist/data/traces/otel/OtelTraceTable.cjs.map +1 -1
  27. package/dist/data/traces/otel/OtelTraceTable.d.ts +14 -3
  28. package/dist/data/traces/otel/OtelTraceTable.d.ts.map +1 -1
  29. package/dist/data/traces/otel/OtelTraceTable.js +44 -8
  30. package/dist/data/traces/otel/OtelTraceTable.js.map +1 -1
  31. package/dist/data/traces/otel/profile-listing.cjs +61 -0
  32. package/dist/data/traces/otel/profile-listing.cjs.map +1 -0
  33. package/dist/data/traces/otel/profile-listing.d.ts +23 -0
  34. package/dist/data/traces/otel/profile-listing.d.ts.map +1 -0
  35. package/dist/data/traces/otel/profile-listing.js +60 -0
  36. package/dist/data/traces/otel/profile-listing.js.map +1 -0
  37. package/dist/data/traces/otel/profile-spans.cjs +22 -0
  38. package/dist/data/traces/otel/profile-spans.cjs.map +1 -0
  39. package/dist/data/traces/otel/profile-spans.d.ts +4 -0
  40. package/dist/data/traces/otel/profile-spans.d.ts.map +1 -0
  41. package/dist/data/traces/otel/profile-spans.js +22 -0
  42. package/dist/data/traces/otel/profile-spans.js.map +1 -0
  43. package/dist/data/traces/otel/span-traces.cjs +7 -1
  44. package/dist/data/traces/otel/span-traces.cjs.map +1 -1
  45. package/dist/data/traces/otel/span-traces.d.ts +2 -0
  46. package/dist/data/traces/otel/span-traces.d.ts.map +1 -1
  47. package/dist/data/traces/otel/span-traces.js +7 -2
  48. package/dist/data/traces/otel/span-traces.js.map +1 -1
  49. package/dist/data/useClickyTableMenu.cjs +33 -0
  50. package/dist/data/useClickyTableMenu.cjs.map +1 -0
  51. package/dist/data/useClickyTableMenu.d.ts +17 -0
  52. package/dist/data/useClickyTableMenu.d.ts.map +1 -0
  53. package/dist/data/useClickyTableMenu.js +33 -0
  54. package/dist/data/useClickyTableMenu.js.map +1 -0
  55. package/dist/data/version-info.cjs +3 -3
  56. package/dist/data/version-info.js +3 -3
  57. package/dist/data.cjs +7 -2
  58. package/dist/data.js +5 -3
  59. package/dist/index.cjs +14 -3
  60. package/dist/index.js +9 -4
  61. package/dist/rpc/CommandForm.cjs +1 -1
  62. package/dist/rpc/CommandForm.cjs.map +1 -1
  63. package/dist/rpc/CommandForm.js +1 -1
  64. package/dist/rpc/CommandForm.js.map +1 -1
  65. package/dist/rpc/EntityExplorerApp.cjs +49 -12
  66. package/dist/rpc/EntityExplorerApp.cjs.map +1 -1
  67. package/dist/rpc/EntityExplorerApp.d.ts +10 -1
  68. package/dist/rpc/EntityExplorerApp.d.ts.map +1 -1
  69. package/dist/rpc/EntityExplorerApp.js +51 -14
  70. package/dist/rpc/EntityExplorerApp.js.map +1 -1
  71. package/dist/rpc/EntitySurface.cjs +38 -0
  72. package/dist/rpc/EntitySurface.cjs.map +1 -0
  73. package/dist/rpc/EntitySurface.d.ts +17 -0
  74. package/dist/rpc/EntitySurface.d.ts.map +1 -0
  75. package/dist/rpc/EntitySurface.js +38 -0
  76. package/dist/rpc/EntitySurface.js.map +1 -0
  77. package/dist/rpc/ExplorerNav.cjs +48 -0
  78. package/dist/rpc/ExplorerNav.cjs.map +1 -0
  79. package/dist/rpc/ExplorerNav.d.ts +12 -0
  80. package/dist/rpc/ExplorerNav.d.ts.map +1 -0
  81. package/dist/rpc/ExplorerNav.js +48 -0
  82. package/dist/rpc/ExplorerNav.js.map +1 -0
  83. package/dist/rpc/explorer-nav-context.cjs +5 -0
  84. package/dist/rpc/explorer-nav-context.cjs.map +1 -0
  85. package/dist/rpc/explorer-nav-context.d.ts +11 -0
  86. package/dist/rpc/explorer-nav-context.d.ts.map +1 -0
  87. package/dist/rpc/explorer-nav-context.js +6 -0
  88. package/dist/rpc/explorer-nav-context.js.map +1 -0
  89. package/dist/rpc.cjs +4 -0
  90. package/dist/rpc.d.ts +2 -0
  91. package/dist/rpc.d.ts.map +1 -1
  92. package/dist/rpc.js +3 -1
  93. package/dist/styles.css +1 -1
  94. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CommandForm.js","names":[],"sources":["../../src/rpc/CommandForm.tsx"],"sourcesContent":["import { useReducer, type FormEvent, type KeyboardEvent, type ReactNode } from \"react\";\nimport { Button } from \"../components/button\";\nimport { Icon } from \"../data/Icon\";\nimport { UiClose } from \"../icons\";\nimport { parameterPlaceholder, type OpenAPIParameter, type OperationRequestValues } from \"./types\";\nimport { normalizeParameters, pathParamNames, submitValue } from \"./command-form-utils\";\nimport { packParameterValues, type ParameterValues } from \"./formMetadata\";\n\nexport type CommandFormSubmission = {\n values: ParameterValues;\n request: OperationRequestValues;\n headers: Record<string, string>;\n};\n\nexport type CommandFormProps = {\n parameters: OpenAPIParameter[];\n onExecute: (submission: CommandFormSubmission) => void;\n isPending: boolean;\n method: string;\n path: string;\n accept: string;\n initialValues?: Record<string, string>;\n};\n\ntype FormState = Record<string, string>;\ntype FormAction = { name: string; value: string };\n\nfunction formReducer(state: FormState, action: FormAction): FormState {\n return { ...state, [action.name]: action.value };\n}\n\nexport function CommandForm({\n parameters,\n onExecute,\n isPending,\n method: _method,\n path,\n accept,\n initialValues,\n}: CommandFormProps) {\n const [values, dispatch] = useReducer(\n formReducer,\n { parameters, path, initialValues },\n ({ parameters: params, path: formPath, initialValues: overrides }) =>\n buildInitialState(normalizeParameters(params, formPath), formPath, overrides),\n );\n\n const formParameters = normalizeParameters(parameters, path);\n const visibleParams = formParameters.filter((p) => !(p.in === \"path\" && initialValues?.[p.name]));\n const inlineLayout = visibleParams.length >= INLINE_LAYOUT_THRESHOLD;\n\n function handleSubmit(event: FormEvent) {\n event.preventDefault();\n const submittedValues: ParameterValues = {};\n for (const param of formParameters) {\n const value = submitValue(param, values[param.name]);\n if (value == null) continue;\n submittedValues[param.name] = value;\n }\n onExecute({\n values: submittedValues,\n request: packParameterValues(submittedValues, formParameters),\n headers: { Accept: accept },\n });\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {visibleParams.length > 0 && (\n <div className={inlineLayout ? \"grid gap-2\" : \"grid gap-4\"}>\n {visibleParams.map((param) => (\n <ParameterField\n key={param.name}\n param={param}\n value={values[param.name] || \"\"}\n inline={inlineLayout}\n onChange={(value) => dispatch({ name: param.name, value })}\n />\n ))}\n </div>\n )}\n\n <div\n className={\n inlineLayout\n ? \"sticky bottom-0 z-10 -mx-2 flex justify-end border-t border-border bg-background/95 px-2 py-3 backdrop-blur-sm\"\n : \"flex justify-end\"\n }\n >\n <Button type=\"submit\" disabled={isPending}>\n {isPending ? \"Executing...\" : \"Execute\"}\n </Button>\n </div>\n </form>\n );\n}\n\nconst INLINE_LAYOUT_THRESHOLD = 6;\n\nfunction ParameterField({\n param,\n value,\n inline,\n onChange,\n}: {\n param: OpenAPIParameter;\n value: string;\n inline: boolean;\n onChange: (value: string) => void;\n}) {\n const schema = param.schema;\n const fieldId = `param-${param.name}`;\n\n if (isMultiValueParam(param)) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <TagInput\n id={fieldId}\n value={value}\n placeholder={parameterPlaceholder(param) || param.name}\n onChange={onChange}\n />\n </FieldWrapper>\n );\n }\n\n if (schema?.enum) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <select\n id={fieldId}\n value={value}\n className={inputClassName}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">Select {param.name}</option>\n {schema.enum.map((v) => (\n <option key={String(v)} value={String(v)}>\n {String(v)}\n </option>\n ))}\n </select>\n </FieldWrapper>\n );\n }\n\n if (schema?.type === \"boolean\") {\n const checkbox = (\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={value === \"true\"}\n onChange={(event) => onChange(event.target.checked ? \"true\" : \"false\")}\n />\n );\n if (inline) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline>\n <div className=\"flex h-9 items-center\">{checkbox}</div>\n </FieldWrapper>\n );\n }\n return (\n <div className=\"flex items-center gap-2\">\n {checkbox}\n <label htmlFor={fieldId} className=\"text-sm font-medium\">\n {param.name}\n {param.required && <span className=\"text-destructive\"> *</span>}\n </label>\n {param.description && (\n <span className=\"text-xs text-muted-foreground\">{param.description}</span>\n )}\n </div>\n );\n }\n\n if (isDateParam(param)) {\n const dateTime = schema?.format === \"date-time\";\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <div className=\"flex gap-2\">\n <input\n id={fieldId}\n type={dateTime ? \"datetime-local\" : \"date\"}\n value={dateInputValue(value, dateTime)}\n className={inputClassName}\n onChange={(event) => onChange(dateOutputValue(event.target.value, dateTime))}\n />\n {value && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n aria-label={`Clear ${param.name}`}\n onClick={() => onChange(\"\")}\n >\n <Icon icon={UiClose} />\n </Button>\n )}\n </div>\n </FieldWrapper>\n );\n }\n\n const inputType = schema?.type === \"integer\" || schema?.type === \"number\" ? \"number\" : \"text\";\n\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <input\n id={fieldId}\n type={inputType}\n value={value}\n className={inputClassName}\n onChange={(event) => onChange(event.target.value)}\n placeholder={\n schema?.default != null\n ? String(schema.default)\n : parameterPlaceholder(param) || param.name\n }\n />\n </FieldWrapper>\n );\n}\n\nfunction TagInput({\n id,\n value,\n placeholder,\n onChange,\n}: {\n id: string;\n value: string;\n placeholder: string;\n onChange: (value: string) => void;\n}) {\n const tags = parseTags(value);\n\n function commit(raw: string, input: HTMLInputElement) {\n const next = raw\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n if (next.length === 0) return;\n onChange(serializeTags([...tags, ...next]));\n input.value = \"\";\n }\n\n function remove(index: number) {\n onChange(serializeTags(tags.filter((_, i) => i !== index)));\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n if (event.key === \"Enter\" || event.key === \",\") {\n event.preventDefault();\n commit(input.value, input);\n return;\n }\n if (event.key === \"Backspace\" && input.value === \"\" && tags.length > 0) {\n remove(tags.length - 1);\n }\n }\n\n return (\n <div className=\"flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent px-2 py-1 shadow-sm\">\n {tags.map((tag, index) => (\n <span\n key={`${tag}-${index}`}\n className=\"inline-flex h-6 max-w-full items-center gap-1 rounded-md bg-muted px-2 text-xs\"\n >\n <span className=\"truncate\">{tag}</span>\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground\"\n aria-label={`Remove ${tag}`}\n onClick={() => remove(index)}\n >\n <Icon icon={UiClose} />\n </button>\n </span>\n ))}\n <input\n id={id}\n className=\"min-w-0 flex-1 basis-32 bg-transparent px-1 py-1 text-sm outline-none\"\n placeholder={tags.length === 0 ? placeholder : \"\"}\n onKeyDown={handleKeyDown}\n onBlur={(event) => commit(event.currentTarget.value, event.currentTarget)}\n />\n </div>\n );\n}\n\nfunction FieldWrapper({\n param,\n fieldId,\n inline,\n children,\n}: {\n param: OpenAPIParameter;\n fieldId: string;\n inline?: boolean;\n children: ReactNode;\n}) {\n const label = (\n <label htmlFor={fieldId} className=\"text-sm font-medium\">\n {param.name}\n {param.required && <span className=\"text-destructive\"> *</span>}\n </label>\n );\n\n if (inline) {\n return (\n <div className=\"grid grid-cols-[10rem_1fr] items-start gap-x-3 gap-y-0.5\">\n <div className=\"flex h-9 items-center\">{label}</div>\n <div className=\"min-w-0\">{children}</div>\n {param.description && (\n <p className=\"col-start-2 text-xs text-muted-foreground\">{param.description}</p>\n )}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1.5\">\n {label}\n {children}\n {param.description && <p className=\"text-xs text-muted-foreground\">{param.description}</p>}\n </div>\n );\n}\n\nfunction buildInitialState(\n parameters: OpenAPIParameter[],\n path: string,\n overrides?: Record<string, string>,\n): FormState {\n const state: FormState = {};\n for (const p of parameters) {\n state[p.name] = initialParamValue(p, overrides?.[p.name]);\n }\n for (const name of pathParamNames(path)) {\n if (state[name] == null) {\n state[name] = overrides?.[name] ?? \"\";\n }\n }\n return state;\n}\n\nfunction initialParamValue(param: OpenAPIParameter, override: string | undefined): string {\n if (override != null) return sanitizeInitialValue(param, override);\n const value = param.schema?.default;\n if (value == null) return \"\";\n return sanitizeInitialValue(param, String(value));\n}\n\nfunction sanitizeInitialValue(param: OpenAPIParameter, value: string): string {\n const trimmed = value.trim();\n if (trimmed === \"[]\" || trimmed === \"null\") return \"\";\n if (isDateParam(param) && isZeroDate(trimmed)) return \"\";\n return value;\n}\n\nfunction isMultiValueParam(param: OpenAPIParameter): boolean {\n const text = `${param.name} ${param.description ?? \"\"}`.toLowerCase();\n return (\n param.schema?.type === \"array\" ||\n String(param.schema?.default ?? \"\") === \"[]\" ||\n text.includes(\"repeatable\")\n );\n}\n\nfunction parseTags(value: string): string[] {\n const trimmed = value.trim();\n if (!trimmed || trimmed === \"[]\" || trimmed === \"null\") return [];\n try {\n const parsed = JSON.parse(trimmed);\n if (Array.isArray(parsed)) return parsed.map(String).filter(Boolean);\n } catch {\n // Fall back to comma-delimited tags below.\n }\n return trimmed\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nfunction serializeTags(tags: string[]): string {\n return tags.join(\",\");\n}\n\nfunction isDateParam(param: OpenAPIParameter): boolean {\n const format = param.schema?.format?.toLowerCase();\n const text = `${param.name} ${param.description ?? \"\"}`.toLowerCase();\n return format === \"date\" || format === \"date-time\" || text.includes(\"date\");\n}\n\nfunction isZeroDate(value: string): boolean {\n return value.startsWith(\"0001-01-01\") || value === \"0001-01-01\";\n}\n\nfunction dateInputValue(value: string, dateTime: boolean): string {\n if (!value || value.startsWith(\"0001-01-01\")) return \"\";\n if (!dateTime) return value.slice(0, 10);\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return value.slice(0, 16);\n const offset = parsed.getTimezoneOffset() * 60_000;\n return new Date(parsed.getTime() - offset).toISOString().slice(0, 16);\n}\n\nfunction dateOutputValue(value: string, dateTime: boolean): string {\n if (!value) return \"\";\n if (!dateTime) return value;\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return value;\n return parsed.toISOString();\n}\n\nconst inputClassName =\n \"h-9 w-full min-w-0 rounded-md border border-input bg-background px-2 text-sm text-foreground outline-none placeholder:text-placeholder disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"CommandForm.js","names":[],"sources":["../../src/rpc/CommandForm.tsx"],"sourcesContent":["import { useReducer, type FormEvent, type KeyboardEvent, type ReactNode } from \"react\";\nimport { Button } from \"../components/button\";\nimport { Icon } from \"../data/Icon\";\nimport { UiClose } from \"../icons\";\nimport { parameterPlaceholder, type OpenAPIParameter, type OperationRequestValues } from \"./types\";\nimport { normalizeParameters, pathParamNames, submitValue } from \"./command-form-utils\";\nimport { packParameterValues, type ParameterValues } from \"./formMetadata\";\n\nexport type CommandFormSubmission = {\n values: ParameterValues;\n request: OperationRequestValues;\n headers: Record<string, string>;\n};\n\nexport type CommandFormProps = {\n parameters: OpenAPIParameter[];\n onExecute: (submission: CommandFormSubmission) => void;\n isPending: boolean;\n method: string;\n path: string;\n accept: string;\n initialValues?: Record<string, string>;\n};\n\ntype FormState = Record<string, string>;\ntype FormAction = { name: string; value: string };\n\nfunction formReducer(state: FormState, action: FormAction): FormState {\n return { ...state, [action.name]: action.value };\n}\n\nexport function CommandForm({\n parameters,\n onExecute,\n isPending,\n method: _method,\n path,\n accept,\n initialValues,\n}: CommandFormProps) {\n const [values, dispatch] = useReducer(\n formReducer,\n { parameters, path, initialValues },\n ({ parameters: params, path: formPath, initialValues: overrides }) =>\n buildInitialState(normalizeParameters(params, formPath), formPath, overrides),\n );\n\n const formParameters = normalizeParameters(parameters, path);\n const visibleParams = formParameters.filter((p) => !(p.in === \"path\" && initialValues?.[p.name]));\n const inlineLayout = visibleParams.length >= INLINE_LAYOUT_THRESHOLD;\n\n function handleSubmit(event: FormEvent) {\n event.preventDefault();\n const submittedValues: ParameterValues = {};\n for (const param of formParameters) {\n const value = submitValue(param, values[param.name]);\n if (value == null) continue;\n submittedValues[param.name] = value;\n }\n onExecute({\n values: submittedValues,\n request: packParameterValues(submittedValues, formParameters),\n headers: { Accept: accept },\n });\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {visibleParams.length > 0 && (\n <div className={inlineLayout ? \"grid gap-2\" : \"grid gap-4\"}>\n {visibleParams.map((param) => (\n <ParameterField\n key={param.name}\n param={param}\n value={values[param.name] || \"\"}\n inline={inlineLayout}\n onChange={(value) => dispatch({ name: param.name, value })}\n />\n ))}\n </div>\n )}\n\n <div\n className={\n inlineLayout\n ? \"sticky bottom-0 z-10 flex justify-end border-t border-border bg-background/95 py-3 backdrop-blur-sm\"\n : \"flex justify-end\"\n }\n >\n <Button type=\"submit\" disabled={isPending}>\n {isPending ? \"Executing...\" : \"Execute\"}\n </Button>\n </div>\n </form>\n );\n}\n\nconst INLINE_LAYOUT_THRESHOLD = 6;\n\nfunction ParameterField({\n param,\n value,\n inline,\n onChange,\n}: {\n param: OpenAPIParameter;\n value: string;\n inline: boolean;\n onChange: (value: string) => void;\n}) {\n const schema = param.schema;\n const fieldId = `param-${param.name}`;\n\n if (isMultiValueParam(param)) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <TagInput\n id={fieldId}\n value={value}\n placeholder={parameterPlaceholder(param) || param.name}\n onChange={onChange}\n />\n </FieldWrapper>\n );\n }\n\n if (schema?.enum) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <select\n id={fieldId}\n value={value}\n className={inputClassName}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">Select {param.name}</option>\n {schema.enum.map((v) => (\n <option key={String(v)} value={String(v)}>\n {String(v)}\n </option>\n ))}\n </select>\n </FieldWrapper>\n );\n }\n\n if (schema?.type === \"boolean\") {\n const checkbox = (\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={value === \"true\"}\n onChange={(event) => onChange(event.target.checked ? \"true\" : \"false\")}\n />\n );\n if (inline) {\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline>\n <div className=\"flex h-9 items-center\">{checkbox}</div>\n </FieldWrapper>\n );\n }\n return (\n <div className=\"flex items-center gap-2\">\n {checkbox}\n <label htmlFor={fieldId} className=\"text-sm font-medium\">\n {param.name}\n {param.required && <span className=\"text-destructive\"> *</span>}\n </label>\n {param.description && (\n <span className=\"text-xs text-muted-foreground\">{param.description}</span>\n )}\n </div>\n );\n }\n\n if (isDateParam(param)) {\n const dateTime = schema?.format === \"date-time\";\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <div className=\"flex gap-2\">\n <input\n id={fieldId}\n type={dateTime ? \"datetime-local\" : \"date\"}\n value={dateInputValue(value, dateTime)}\n className={inputClassName}\n onChange={(event) => onChange(dateOutputValue(event.target.value, dateTime))}\n />\n {value && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n aria-label={`Clear ${param.name}`}\n onClick={() => onChange(\"\")}\n >\n <Icon icon={UiClose} />\n </Button>\n )}\n </div>\n </FieldWrapper>\n );\n }\n\n const inputType = schema?.type === \"integer\" || schema?.type === \"number\" ? \"number\" : \"text\";\n\n return (\n <FieldWrapper param={param} fieldId={fieldId} inline={inline}>\n <input\n id={fieldId}\n type={inputType}\n value={value}\n className={inputClassName}\n onChange={(event) => onChange(event.target.value)}\n placeholder={\n schema?.default != null\n ? String(schema.default)\n : parameterPlaceholder(param) || param.name\n }\n />\n </FieldWrapper>\n );\n}\n\nfunction TagInput({\n id,\n value,\n placeholder,\n onChange,\n}: {\n id: string;\n value: string;\n placeholder: string;\n onChange: (value: string) => void;\n}) {\n const tags = parseTags(value);\n\n function commit(raw: string, input: HTMLInputElement) {\n const next = raw\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n if (next.length === 0) return;\n onChange(serializeTags([...tags, ...next]));\n input.value = \"\";\n }\n\n function remove(index: number) {\n onChange(serializeTags(tags.filter((_, i) => i !== index)));\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n if (event.key === \"Enter\" || event.key === \",\") {\n event.preventDefault();\n commit(input.value, input);\n return;\n }\n if (event.key === \"Backspace\" && input.value === \"\" && tags.length > 0) {\n remove(tags.length - 1);\n }\n }\n\n return (\n <div className=\"flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent px-2 py-1 shadow-sm\">\n {tags.map((tag, index) => (\n <span\n key={`${tag}-${index}`}\n className=\"inline-flex h-6 max-w-full items-center gap-1 rounded-md bg-muted px-2 text-xs\"\n >\n <span className=\"truncate\">{tag}</span>\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:text-foreground\"\n aria-label={`Remove ${tag}`}\n onClick={() => remove(index)}\n >\n <Icon icon={UiClose} />\n </button>\n </span>\n ))}\n <input\n id={id}\n className=\"min-w-0 flex-1 basis-32 bg-transparent px-1 py-1 text-sm outline-none\"\n placeholder={tags.length === 0 ? placeholder : \"\"}\n onKeyDown={handleKeyDown}\n onBlur={(event) => commit(event.currentTarget.value, event.currentTarget)}\n />\n </div>\n );\n}\n\nfunction FieldWrapper({\n param,\n fieldId,\n inline,\n children,\n}: {\n param: OpenAPIParameter;\n fieldId: string;\n inline?: boolean;\n children: ReactNode;\n}) {\n const label = (\n <label htmlFor={fieldId} className=\"text-sm font-medium\">\n {param.name}\n {param.required && <span className=\"text-destructive\"> *</span>}\n </label>\n );\n\n if (inline) {\n return (\n <div className=\"grid grid-cols-[10rem_1fr] items-start gap-x-3 gap-y-0.5\">\n <div className=\"flex h-9 items-center\">{label}</div>\n <div className=\"min-w-0\">{children}</div>\n {param.description && (\n <p className=\"col-start-2 text-xs text-muted-foreground\">{param.description}</p>\n )}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-1.5\">\n {label}\n {children}\n {param.description && <p className=\"text-xs text-muted-foreground\">{param.description}</p>}\n </div>\n );\n}\n\nfunction buildInitialState(\n parameters: OpenAPIParameter[],\n path: string,\n overrides?: Record<string, string>,\n): FormState {\n const state: FormState = {};\n for (const p of parameters) {\n state[p.name] = initialParamValue(p, overrides?.[p.name]);\n }\n for (const name of pathParamNames(path)) {\n if (state[name] == null) {\n state[name] = overrides?.[name] ?? \"\";\n }\n }\n return state;\n}\n\nfunction initialParamValue(param: OpenAPIParameter, override: string | undefined): string {\n if (override != null) return sanitizeInitialValue(param, override);\n const value = param.schema?.default;\n if (value == null) return \"\";\n return sanitizeInitialValue(param, String(value));\n}\n\nfunction sanitizeInitialValue(param: OpenAPIParameter, value: string): string {\n const trimmed = value.trim();\n if (trimmed === \"[]\" || trimmed === \"null\") return \"\";\n if (isDateParam(param) && isZeroDate(trimmed)) return \"\";\n return value;\n}\n\nfunction isMultiValueParam(param: OpenAPIParameter): boolean {\n const text = `${param.name} ${param.description ?? \"\"}`.toLowerCase();\n return (\n param.schema?.type === \"array\" ||\n String(param.schema?.default ?? \"\") === \"[]\" ||\n text.includes(\"repeatable\")\n );\n}\n\nfunction parseTags(value: string): string[] {\n const trimmed = value.trim();\n if (!trimmed || trimmed === \"[]\" || trimmed === \"null\") return [];\n try {\n const parsed = JSON.parse(trimmed);\n if (Array.isArray(parsed)) return parsed.map(String).filter(Boolean);\n } catch {\n // Fall back to comma-delimited tags below.\n }\n return trimmed\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nfunction serializeTags(tags: string[]): string {\n return tags.join(\",\");\n}\n\nfunction isDateParam(param: OpenAPIParameter): boolean {\n const format = param.schema?.format?.toLowerCase();\n const text = `${param.name} ${param.description ?? \"\"}`.toLowerCase();\n return format === \"date\" || format === \"date-time\" || text.includes(\"date\");\n}\n\nfunction isZeroDate(value: string): boolean {\n return value.startsWith(\"0001-01-01\") || value === \"0001-01-01\";\n}\n\nfunction dateInputValue(value: string, dateTime: boolean): string {\n if (!value || value.startsWith(\"0001-01-01\")) return \"\";\n if (!dateTime) return value.slice(0, 10);\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return value.slice(0, 16);\n const offset = parsed.getTimezoneOffset() * 60_000;\n return new Date(parsed.getTime() - offset).toISOString().slice(0, 16);\n}\n\nfunction dateOutputValue(value: string, dateTime: boolean): string {\n if (!value) return \"\";\n if (!dateTime) return value;\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return value;\n return parsed.toISOString();\n}\n\nconst inputClassName =\n \"h-9 w-full min-w-0 rounded-md border border-input bg-background px-2 text-sm text-foreground outline-none placeholder:text-placeholder disabled:cursor-not-allowed disabled:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring\";\n"],"mappings":"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"}
@@ -4,6 +4,7 @@ const require_AppShell = require("../layout/AppShell.cjs");
4
4
  const require_useOperations = require("./useOperations.cjs");
5
5
  const require_clickyMetadata = require("./clickyMetadata.cjs");
6
6
  const require_OperationCatalog = require("./OperationCatalog.cjs");
7
+ const require_explorer_nav_context = require("./explorer-nav-context.cjs");
7
8
  const require_commandHref = require("./commandHref.cjs");
8
9
  const require_OperationCommandPage = require("./OperationCommandPage.cjs");
9
10
  const require_OperationEntityPage = require("./OperationEntityPage.cjs");
@@ -12,7 +13,7 @@ let react = require("react");
12
13
  let react_jsx_runtime = require("react/jsx-runtime");
13
14
  // src/rpc/EntityExplorerApp.tsx
14
15
  var SIDEBAR_COLLAPSED_KEY = "clicky-ui:sidebar:collapsed";
15
- function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer }) {
16
+ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer, surfaceTitleActions, surfacePaginationMode }) {
16
17
  const formPre = formExtensions?.pre;
17
18
  const formPost = formExtensions?.post;
18
19
  const { pathname, renderLink, navigate } = require_router.useRouter();
@@ -64,6 +65,26 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
64
65
  showApiExplorer,
65
66
  explorerDefinition
66
67
  ]);
68
+ const [navContainer, setNavContainer] = (0, react.useState)(null);
69
+ const [navClaims, setNavClaims] = (0, react.useState)(0);
70
+ const claimNav = (0, react.useCallback)(() => {
71
+ setNavClaims((count) => count + 1);
72
+ return () => setNavClaims((count) => count - 1);
73
+ }, []);
74
+ const surface = resolvedRoute.kind === "surface" ? resolvedRoute.surface : void 0;
75
+ const titleActions = surface ? surfaceTitleActions?.(surface.key) : void 0;
76
+ const paginationMode = surface ? surfacePaginationMode?.(surface.key) : void 0;
77
+ const navSlot = (0, react.useMemo)(() => ({
78
+ container: surface ? navContainer : null,
79
+ title: surface?.title ?? "",
80
+ titleActions,
81
+ claim: claimNav
82
+ }), [
83
+ claimNav,
84
+ navContainer,
85
+ surface,
86
+ titleActions
87
+ ]);
67
88
  (0, react.useEffect)(() => {
68
89
  if (defaultSurface == null || relativePath !== "/") return;
69
90
  navigate(require_commandHref.withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });
@@ -73,7 +94,7 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
73
94
  relativePath,
74
95
  navigate
75
96
  ]);
76
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AppShell.AppShell, {
97
+ const content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AppShell.AppShell, {
77
98
  brand: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
78
99
  className: "min-w-0",
79
100
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -87,16 +108,27 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
87
108
  navSections,
88
109
  sidebarFooter: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_theme_switcher.ThemeSwitcher, {}),
89
110
  ...actions ? { actions } : {},
90
- ...resolvedRoute.kind === "surface" && resolvedRoute.surface ? { nav: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
111
+ ...resolvedRoute.kind === "surface" && resolvedRoute.surface ? { nav: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [navClaims === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
91
112
  className: "flex min-w-0 flex-1 items-baseline gap-density-2",
92
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
93
- className: "min-w-0 truncate text-base font-semibold tracking-tight",
94
- children: resolvedRoute.surface.title
95
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
96
- className: "hidden min-w-0 truncate text-sm text-muted-foreground lg:block",
97
- children: require_clickyMetadata.makeSurfaceDefinition(resolvedRoute.surface).description
98
- })]
99
- }) } : {},
113
+ children: [
114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
115
+ className: "min-w-0 truncate text-base font-semibold tracking-tight",
116
+ children: resolvedRoute.surface.title
117
+ }),
118
+ titleActions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
119
+ className: "flex shrink-0 items-center self-center",
120
+ children: titleActions
121
+ }) : null,
122
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
123
+ className: "hidden min-w-0 truncate text-sm text-muted-foreground lg:block",
124
+ children: require_clickyMetadata.makeSurfaceDefinition(resolvedRoute.surface).description
125
+ })
126
+ ]
127
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
128
+ ref: setNavContainer,
129
+ "data-slot": "explorer-nav",
130
+ className: "contents"
131
+ })] }) } : {},
100
132
  collapsedStorageKey: SIDEBAR_COLLAPSED_KEY,
101
133
  contentWidth: "full",
102
134
  contentClassName: "p-6",
@@ -111,7 +143,8 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
111
143
  ...formPost ? { formPost } : {},
112
144
  ...formActions ? { formActions } : {},
113
145
  ...surfaceActionLabels?.[resolvedRoute.surface.key] ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] } : {},
114
- ...resultRenderer ? { resultRenderer } : {}
146
+ ...resultRenderer ? { resultRenderer } : {},
147
+ ...paginationMode ? { paginationMode } : {}
115
148
  }, resolvedRoute.surface.key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UnknownSurface, { surfaceKey: resolvedRoute.surfaceKey }) : resolvedRoute.kind === "entity" ? resolvedRoute.surface ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_OperationEntityPage.OperationEntityPage, {
116
149
  definition: require_clickyMetadata.makeSurfaceDefinition(resolvedRoute.surface),
117
150
  entities: [resolvedRoute.surface.entity],
@@ -152,6 +185,10 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
152
185
  children: "Redirecting…"
153
186
  })
154
187
  });
188
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_explorer_nav_context.ExplorerNavContext.Provider, {
189
+ value: navSlot,
190
+ children: content
191
+ });
155
192
  }
156
193
  function parseExplorerRoute(pathname) {
157
194
  const normalized = require_commandHref.trimTrailingSlash(pathname) || "/";
@@ -1 +1 @@
1
- {"version":3,"file":"EntityExplorerApp.cjs","names":["useRouter","useOperations","useMemo","getClickySurfaces","buildCommandHref","surfaceNavSection","withBasePath","_jsx","AppShell","ThemeSwitcher","makeSurfaceDefinition","OperationCatalog","OperationEntityPage","OperationCommandPage","trimTrailingSlash","ARG_QUERY_PREFIX"],"sources":["../../src/rpc/EntityExplorerApp.tsx"],"sourcesContent":["import { useEffect, useMemo, type ReactNode } from \"react\";\nimport { ThemeSwitcher } from \"../components/theme-switcher\";\nimport type { PreExtension, PostExtension } from \"../components/json-schema-form-types\";\nimport type { ClickyCommandRuntime } from \"../data/Clicky\";\nimport { AppShell, type AppShellNavSection } from \"../layout/AppShell\";\nimport { getClickySurfaces, makeSurfaceDefinition } from \"./clickyMetadata\";\nimport {\n ARG_QUERY_PREFIX,\n AUTORUN_QUERY_PARAM,\n buildCommandHref,\n trimTrailingSlash,\n withBasePath,\n} from \"./commandHref\";\nimport { useRouter } from \"./router\";\nimport { OperationCatalog } from \"./OperationCatalog\";\nimport type { ResultRenderer } from \"./OperationResultView\";\nimport { OperationCommandPage } from \"./OperationCommandPage\";\nimport {\n OperationEntityPage,\n type EntityDetailBodyRenderer,\n type EntityDetailHeaderRenderer,\n} from \"./OperationEntityPage\";\nimport type { FormActionsRenderer } from \"./SchemaActionForm\";\nimport { surfaceNavSection } from \"./surfaceNav\";\nimport type { ClickySurface } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\nimport { useOperations } from \"./useOperations\";\n\nexport type SurfaceActionLabels = Record<string, Record<string, string>>;\n\nexport type EntityExplorerAppProps = {\n client: OperationsApiClient;\n basePath?: string;\n showApiExplorer?: boolean;\n /** Right-aligned top-bar cluster forwarded to AppShell's actions slot. */\n actions?: ReactNode;\n /**\n * Custom JsonSchemaForm field extensions applied to every create/edit form the\n * explorer renders — e.g. a SecretKeySelector widget keyed on an\n * `x-clicky-component` schema hint. `pre` transforms a resolved control; `post`\n * can replace the rendered value node with a custom component.\n */\n formExtensions?: { pre?: PreExtension[]; post?: PostExtension[] };\n /**\n * Optional extra footer actions added to every create/edit form (e.g. a\n * connection \"Test\" split-button). The renderer receives the live form value\n * and the action it submits to, so it can scope itself to a given entity.\n */\n formActions?: FormActionsRenderer;\n /** Per-surface labels keyed by x-clicky verb/actionName. */\n surfaceActionLabels?: SurfaceActionLabels;\n /**\n * @deprecated Superseded by per-surface icons emitted by the backend\n * (x-clicky-icon → ClickySurface.icon). AppShell section labels are plain\n * strings, so this group-header icon map is no longer rendered. Kept for\n * source-compatibility only.\n */\n surfaceIcons?: Record<string, ReactNode>;\n /**\n * Optional host override for the result surface. Called where an entity's\n * result table renders, with the current surface key, the raw response, and\n * the default OperationResultView element. Return `defaultView` to keep the\n * standard table, or a custom node (e.g. a LogsTable for trace/log profiles\n * flagged via x-clicky-render). Filtering/sorting policy is the host's choice.\n */\n resultRenderer?: ResultRenderer;\n /** Host override for a loaded entity's body; heading and actions remain. */\n entityDetailBodyRenderer?: EntityDetailBodyRenderer;\n /** Host override for a loaded entity's \"{title}: {id}\" heading. */\n entityDetailHeaderRenderer?: EntityDetailHeaderRenderer;\n};\n\n// SIDEBAR_COLLAPSED_KEY persists the AppShell rail collapsed flag across reloads.\nconst SIDEBAR_COLLAPSED_KEY = \"clicky-ui:sidebar:collapsed\";\n\nexport function EntityExplorerApp({\n client,\n basePath = \"\",\n showApiExplorer = true,\n actions,\n formExtensions,\n formActions,\n surfaceActionLabels,\n resultRenderer,\n entityDetailBodyRenderer,\n entityDetailHeaderRenderer,\n}: EntityExplorerAppProps) {\n const formPre = formExtensions?.pre;\n const formPost = formExtensions?.post;\n const { pathname, renderLink, navigate } = useRouter();\n const { spec } = useOperations(client);\n const surfaces = useMemo(() => getClickySurfaces(spec), [spec]);\n const surfaceGroups = useMemo(() => groupSurfacesByParent(surfaces), [surfaces]);\n const defaultSurface = useMemo(\n () => surfaces.find((surface) => !surface.admin) ?? surfaces[0],\n [surfaces],\n );\n const relativePath = useMemo(() => stripBasePath(pathname, basePath), [basePath, pathname]);\n const route = useMemo(() => parseExplorerRoute(relativePath), [relativePath]);\n const resolvedRoute = useMemo(() => resolveRouteSurface(route, surfaces), [route, surfaces]);\n const commandRuntime = useMemo<ClickyCommandRuntime>(\n () => ({\n client,\n hrefForCommand: (resolved) => buildCommandHref(basePath, resolved),\n onNavigate: (resolved) => {\n const href = buildCommandHref(basePath, resolved);\n if (!href) return;\n navigate(href);\n },\n }),\n [basePath, client, navigate],\n );\n const { initialValues: commandInitialValues } = useMemo(\n () =>\n resolvedRoute.kind === \"command\"\n ? readCommandQueryParams()\n : { initialValues: {}, autoRun: false },\n [resolvedRoute.kind, pathname],\n );\n const explorerDefinition = useMemo(\n () => ({\n key: \"explorer\",\n title: \"API Explorer\",\n description: \"Every operation exposed by the current OpenAPI spec.\",\n }),\n [],\n );\n const navSections = useMemo<AppShellNavSection[]>(() => {\n const sections: AppShellNavSection[] = surfaceGroups.map((group) =>\n surfaceNavSection(group.label, group.surfaces, {\n isActive: (surface) => isSurfaceRouteActive(resolvedRoute, surface.key),\n hrefFor: (surface) => withBasePath(basePath, `/${surface.key}`),\n }),\n );\n if (showApiExplorer) {\n sections.push({\n items: [\n {\n key: explorerDefinition.key,\n label: explorerDefinition.title,\n active: resolvedRoute.kind === \"explorer\" || resolvedRoute.kind === \"command\",\n to: withBasePath(basePath, \"/explorer\"),\n },\n ],\n });\n }\n return sections;\n }, [surfaceGroups, basePath, resolvedRoute, showApiExplorer, explorerDefinition]);\n\n useEffect(() => {\n if (defaultSurface == null || relativePath !== \"/\") return;\n navigate(withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });\n }, [basePath, defaultSurface, relativePath, navigate]);\n\n const content = (\n <AppShell\n brand={\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold\">\n {spec?.info.title ?? \"Clicky Explorer\"}\n </div>\n <div className=\"truncate text-xs text-sidebar-foreground/60\">\n Metadata-driven entity explorer\n </div>\n </div>\n }\n navSections={navSections}\n sidebarFooter={<ThemeSwitcher />}\n {...(actions ? { actions } : {})}\n // The page header belongs to the host, not to OperationCatalog — the\n // catalog renders only its results. OperationEntityPage still titles\n // itself, so this covers the collection route only.\n //\n // It rides in the top bar rather than a bodyHeader row: next to the rail\n // that bar is otherwise empty, and the row it replaces cost the result\n // table ~72px of the viewport it needs to keep its sticky head and\n // pagination footer on screen without the page itself scrolling.\n {...(resolvedRoute.kind === \"surface\" && resolvedRoute.surface\n ? {\n nav: (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-density-2\">\n <h1 className=\"min-w-0 truncate text-base font-semibold tracking-tight\">\n {resolvedRoute.surface.title}\n </h1>\n <p className=\"hidden min-w-0 truncate text-sm text-muted-foreground lg:block\">\n {makeSurfaceDefinition(resolvedRoute.surface).description}\n </p>\n </div>\n ),\n }\n : {})}\n collapsedStorageKey={SIDEBAR_COLLAPSED_KEY}\n // Results are wide tables, not prose — let them use the whole workspace\n // instead of the centred reading column.\n contentWidth=\"full\"\n contentClassName=\"p-6\"\n >\n {resolvedRoute.kind === \"surface\" ? (\n resolvedRoute.surface ? (\n <OperationCatalog\n // Filter state is seeded from the URL when the catalog mounts and\n // written back to it, so one instance spanning two surfaces would\n // carry the first surface's filters onto the second's URL. Keying\n // on the surface makes each one start from its own query string.\n key={resolvedRoute.surface.key}\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(resultRenderer ? { resultRenderer } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"entity\" ? (\n resolvedRoute.surface ? (\n <OperationEntityPage\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n backHref={withBasePath(basePath, `/${resolvedRoute.surface.key}`)}\n backLabel={`Back to ${resolvedRoute.surface.title}`}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(entityDetailBodyRenderer ? { entityDetailBodyRenderer } : {})}\n {...(entityDetailHeaderRenderer ? { entityDetailHeaderRenderer } : {})}\n {...(resolvedRoute.id ? { id: resolvedRoute.id } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"command\" ? (\n <OperationCommandPage\n client={client}\n backHref={withBasePath(basePath, \"/explorer\")}\n backLabel=\"Back to API Explorer\"\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n initialValues={commandInitialValues}\n autoRun={true}\n {...(resolvedRoute.operationId ? { operationId: resolvedRoute.operationId } : {})}\n />\n ) : resolvedRoute.kind === \"explorer\" ? (\n <OperationCatalog\n definition={explorerDefinition}\n entities={[]}\n allOperations\n client={client}\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n />\n ) : defaultSurface == null ? (\n <div className=\"text-sm text-muted-foreground\">Loading surfaces…</div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">Redirecting…</div>\n )}\n </AppShell>\n );\n\n return content;\n}\n\ntype ExplorerRoute =\n | { kind: \"root\" }\n | { kind: \"explorer\" }\n | { kind: \"command\"; operationId?: string }\n | { kind: \"surface\"; surfaceKey: string; surface?: ClickySurface }\n | {\n kind: \"entity\";\n surfaceKey: string;\n surface?: ClickySurface;\n id?: string;\n };\n\nfunction parseExplorerRoute(pathname: string): ExplorerRoute {\n const normalized = trimTrailingSlash(pathname) || \"/\";\n if (normalized === \"/\") {\n return { kind: \"root\" };\n }\n\n const segments = normalized.replace(/^\\/+/, \"\").split(\"/\");\n if (segments[0] === \"explorer\") {\n return { kind: \"explorer\" };\n }\n if (segments[0] === \"commands\") {\n return segments[1] ? { kind: \"command\", operationId: segments[1] } : { kind: \"command\" };\n }\n const surfaceKey = segments[0] ?? \"\";\n if (segments[1]) {\n return {\n kind: \"entity\",\n surfaceKey,\n id: decodeURIComponent(segments[1]),\n };\n }\n return { kind: \"surface\", surfaceKey };\n}\n\nfunction resolveRouteSurface(route: ExplorerRoute, surfaces: ClickySurface[]): ExplorerRoute {\n if (route.kind !== \"surface\" && route.kind !== \"entity\") {\n return route;\n }\n const surface = surfaces.find((item) => item.key === route.surfaceKey);\n return surface ? { ...route, surface } : route;\n}\n\nfunction isSurfaceRouteActive(route: ExplorerRoute, surfaceKey: string) {\n return (\n (route.kind === \"surface\" && route.surfaceKey === surfaceKey) ||\n (route.kind === \"entity\" && route.surfaceKey === surfaceKey)\n );\n}\n\nfunction stripBasePath(pathname: string, basePath: string) {\n const base = trimTrailingSlash(basePath);\n if (!base || base === \"/\") {\n return trimTrailingSlash(pathname) || \"/\";\n }\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return trimTrailingSlash(pathname.slice(base.length)) || \"/\";\n }\n return trimTrailingSlash(pathname) || \"/\";\n}\n\nfunction UnknownSurface({ surfaceKey }: { surfaceKey: string }) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n Unknown surface: <code>{surfaceKey}</code>\n </div>\n );\n}\n\nconst LEGACY_AUTORUN_QUERY_PARAM = \"__autoRun\";\n\ntype SurfaceGroup = {\n parent: string; // the slug; \"\" for surfaces that declared no parent\n label: string; // display name in the sidebar\n surfaces: ClickySurface[];\n};\n\n// groupSurfacesByParent buckets surfaces by their ClickySurface.parent slug,\n// preserving the order surfaces appeared in the spec. Surfaces with no parent\n// fall into a trailing \"\" bucket labelled \"Other\".\nfunction groupSurfacesByParent(surfaces: ClickySurface[]): SurfaceGroup[] {\n const order: string[] = [];\n const buckets = new Map<string, ClickySurface[]>();\n for (const surface of surfaces) {\n const parent = surface.parent ?? \"\";\n if (!buckets.has(parent)) {\n buckets.set(parent, []);\n order.push(parent);\n }\n buckets.get(parent)!.push(surface);\n }\n return order.map((parent) => ({\n parent,\n label: parent === \"\" ? \"Other\" : titleCase(parent),\n surfaces: buckets.get(parent)!,\n }));\n}\n\nfunction titleCase(value: string): string {\n if (!value) return value;\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction readCommandQueryParams(): { initialValues: Record<string, string>; autoRun: boolean } {\n if (typeof window === \"undefined\") {\n return { initialValues: {}, autoRun: false };\n }\n const search = new URLSearchParams(window.location.search);\n const initialValues: Record<string, string> = {};\n let autoRun = false;\n const positional: string[] = [];\n\n for (const [key, value] of search.entries()) {\n if (key === AUTORUN_QUERY_PARAM || key === LEGACY_AUTORUN_QUERY_PARAM) {\n autoRun = value === \"1\" || value === \"true\";\n continue;\n }\n if (key.startsWith(ARG_QUERY_PREFIX)) {\n const index = Number.parseInt(key.slice(ARG_QUERY_PREFIX.length), 10);\n if (Number.isFinite(index) && index >= 0) {\n positional[index] = value;\n }\n continue;\n }\n initialValues[key] = value;\n }\n\n const packedArgs = positional.filter((value) => value !== undefined).join(\" \");\n if (packedArgs !== \"\") {\n initialValues.args = packedArgs;\n }\n\n return { initialValues, autoRun };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EntityExplorerApp.cjs","names":["useRouter","useOperations","useMemo","getClickySurfaces","buildCommandHref","surfaceNavSection","withBasePath","useState","useCallback","AppShell","ThemeSwitcher","_Fragment","makeSurfaceDefinition","OperationCatalog","OperationEntityPage","OperationCommandPage","ExplorerNavContext","trimTrailingSlash","ARG_QUERY_PREFIX"],"sources":["../../src/rpc/EntityExplorerApp.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { ExplorerNavContext, type ExplorerNavSlot } from \"./explorer-nav-context\";\nimport { ThemeSwitcher } from \"../components/theme-switcher\";\nimport type { PreExtension, PostExtension } from \"../components/json-schema-form-types\";\nimport type { ClickyCommandRuntime } from \"../data/Clicky\";\nimport { AppShell, type AppShellNavSection } from \"../layout/AppShell\";\nimport { getClickySurfaces, makeSurfaceDefinition } from \"./clickyMetadata\";\nimport {\n ARG_QUERY_PREFIX,\n AUTORUN_QUERY_PARAM,\n buildCommandHref,\n trimTrailingSlash,\n withBasePath,\n} from \"./commandHref\";\nimport { useRouter } from \"./router\";\nimport { OperationCatalog } from \"./OperationCatalog\";\nimport type { ResultRenderer } from \"./OperationResultView\";\nimport { OperationCommandPage } from \"./OperationCommandPage\";\nimport {\n OperationEntityPage,\n type EntityDetailBodyRenderer,\n type EntityDetailHeaderRenderer,\n} from \"./OperationEntityPage\";\nimport type { FormActionsRenderer } from \"./SchemaActionForm\";\nimport { surfaceNavSection } from \"./surfaceNav\";\nimport type { ClickySurface } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\nimport { useOperations } from \"./useOperations\";\n\nexport type SurfaceActionLabels = Record<string, Record<string, string>>;\n\nexport type EntityExplorerAppProps = {\n client: OperationsApiClient;\n basePath?: string;\n showApiExplorer?: boolean;\n /** Right-aligned top-bar cluster forwarded to AppShell's actions slot. */\n actions?: ReactNode;\n /**\n * Custom JsonSchemaForm field extensions applied to every create/edit form the\n * explorer renders — e.g. a SecretKeySelector widget keyed on an\n * `x-clicky-component` schema hint. `pre` transforms a resolved control; `post`\n * can replace the rendered value node with a custom component.\n */\n formExtensions?: { pre?: PreExtension[]; post?: PostExtension[] };\n /**\n * Optional extra footer actions added to every create/edit form (e.g. a\n * connection \"Test\" split-button). The renderer receives the live form value\n * and the action it submits to, so it can scope itself to a given entity.\n */\n formActions?: FormActionsRenderer;\n /** Per-surface labels keyed by x-clicky verb/actionName. */\n surfaceActionLabels?: SurfaceActionLabels;\n /**\n * @deprecated Superseded by per-surface icons emitted by the backend\n * (x-clicky-icon → ClickySurface.icon). AppShell section labels are plain\n * strings, so this group-header icon map is no longer rendered. Kept for\n * source-compatibility only.\n */\n surfaceIcons?: Record<string, ReactNode>;\n /**\n * Optional host override for the result surface. Called where an entity's\n * result table renders, with the current surface key, the raw response, and\n * the default OperationResultView element. Return `defaultView` to keep the\n * standard table, or a custom node (e.g. a LogsTable for trace/log profiles\n * flagged via x-clicky-render). Filtering/sorting policy is the host's choice.\n */\n resultRenderer?: ResultRenderer;\n /** Host override for a loaded entity's body; heading and actions remain. */\n entityDetailBodyRenderer?: EntityDetailBodyRenderer;\n /** Host override for a loaded entity's \"{title}: {id}\" heading. */\n entityDetailHeaderRenderer?: EntityDetailHeaderRenderer;\n /** Compact host controls (e.g. an edit icon) shown right after a surface's title or breadcrumb in the top bar. */\n surfaceTitleActions?: (surfaceKey: string) => ReactNode;\n /**\n * Per-surface paging presentation, forwarded to OperationCatalog's\n * `paginationMode`. Return undefined to keep automatic selection (a cursor\n * walk whenever the list declares a cursor); return \"paged\" for a surface\n * whose readers need prev/next offset controls.\n */\n surfacePaginationMode?: (surfaceKey: string) => \"paged\" | \"infinite\" | undefined;\n};\n\n// SIDEBAR_COLLAPSED_KEY persists the AppShell rail collapsed flag across reloads.\nconst SIDEBAR_COLLAPSED_KEY = \"clicky-ui:sidebar:collapsed\";\n\nexport function EntityExplorerApp({\n client,\n basePath = \"\",\n showApiExplorer = true,\n actions,\n formExtensions,\n formActions,\n surfaceActionLabels,\n resultRenderer,\n entityDetailBodyRenderer,\n entityDetailHeaderRenderer,\n surfaceTitleActions,\n surfacePaginationMode,\n}: EntityExplorerAppProps) {\n const formPre = formExtensions?.pre;\n const formPost = formExtensions?.post;\n const { pathname, renderLink, navigate } = useRouter();\n const { spec } = useOperations(client);\n const surfaces = useMemo(() => getClickySurfaces(spec), [spec]);\n const surfaceGroups = useMemo(() => groupSurfacesByParent(surfaces), [surfaces]);\n const defaultSurface = useMemo(\n () => surfaces.find((surface) => !surface.admin) ?? surfaces[0],\n [surfaces],\n );\n const relativePath = useMemo(() => stripBasePath(pathname, basePath), [basePath, pathname]);\n const route = useMemo(() => parseExplorerRoute(relativePath), [relativePath]);\n const resolvedRoute = useMemo(() => resolveRouteSurface(route, surfaces), [route, surfaces]);\n const commandRuntime = useMemo<ClickyCommandRuntime>(\n () => ({\n client,\n hrefForCommand: (resolved) => buildCommandHref(basePath, resolved),\n onNavigate: (resolved) => {\n const href = buildCommandHref(basePath, resolved);\n if (!href) return;\n navigate(href);\n },\n }),\n [basePath, client, navigate],\n );\n const { initialValues: commandInitialValues } = useMemo(\n () =>\n resolvedRoute.kind === \"command\"\n ? readCommandQueryParams()\n : { initialValues: {}, autoRun: false },\n [resolvedRoute.kind, pathname],\n );\n const explorerDefinition = useMemo(\n () => ({\n key: \"explorer\",\n title: \"API Explorer\",\n description: \"Every operation exposed by the current OpenAPI spec.\",\n }),\n [],\n );\n const navSections = useMemo<AppShellNavSection[]>(() => {\n const sections: AppShellNavSection[] = surfaceGroups.map((group) =>\n surfaceNavSection(group.label, group.surfaces, {\n isActive: (surface) => isSurfaceRouteActive(resolvedRoute, surface.key),\n hrefFor: (surface) => withBasePath(basePath, `/${surface.key}`),\n }),\n );\n if (showApiExplorer) {\n sections.push({\n items: [\n {\n key: explorerDefinition.key,\n label: explorerDefinition.title,\n active: resolvedRoute.kind === \"explorer\" || resolvedRoute.kind === \"command\",\n to: withBasePath(basePath, \"/explorer\"),\n },\n ],\n });\n }\n return sections;\n }, [surfaceGroups, basePath, resolvedRoute, showApiExplorer, explorerDefinition]);\n\n const [navContainer, setNavContainer] = useState<HTMLElement | null>(null);\n const [navClaims, setNavClaims] = useState(0);\n const claimNav = useCallback(() => {\n setNavClaims((count) => count + 1);\n return () => setNavClaims((count) => count - 1);\n }, []);\n const surface = resolvedRoute.kind === \"surface\" ? resolvedRoute.surface : undefined;\n const titleActions = surface ? surfaceTitleActions?.(surface.key) : undefined;\n const paginationMode = surface ? surfacePaginationMode?.(surface.key) : undefined;\n // The container only exists while a surface is resolved, so a portal\n // mounted before the spec loads renders nothing rather than a bare crumb.\n const navSlot = useMemo<ExplorerNavSlot>(\n () => ({\n container: surface ? navContainer : null,\n title: surface?.title ?? \"\",\n titleActions,\n claim: claimNav,\n }),\n [claimNav, navContainer, surface, titleActions],\n );\n\n useEffect(() => {\n if (defaultSurface == null || relativePath !== \"/\") return;\n navigate(withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });\n }, [basePath, defaultSurface, relativePath, navigate]);\n\n const content = (\n <AppShell\n brand={\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold\">\n {spec?.info.title ?? \"Clicky Explorer\"}\n </div>\n <div className=\"truncate text-xs text-sidebar-foreground/60\">\n Metadata-driven entity explorer\n </div>\n </div>\n }\n navSections={navSections}\n sidebarFooter={<ThemeSwitcher />}\n {...(actions ? { actions } : {})}\n // The page header belongs to the host, not to OperationCatalog — the\n // catalog renders only its results. OperationEntityPage still titles\n // itself, so this covers the collection route only.\n //\n // It rides in the top bar rather than a bodyHeader row: next to the rail\n // that bar is otherwise empty, and the row it replaces cost the result\n // table ~72px of the viewport it needs to keep its sticky head and\n // pagination footer on screen without the page itself scrolling.\n {...(resolvedRoute.kind === \"surface\" && resolvedRoute.surface\n ? {\n nav: (\n <>\n {navClaims === 0 && (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-density-2\">\n <h1 className=\"min-w-0 truncate text-base font-semibold tracking-tight\">\n {resolvedRoute.surface.title}\n </h1>\n {titleActions ? (\n <div className=\"flex shrink-0 items-center self-center\">{titleActions}</div>\n ) : null}\n <p className=\"hidden min-w-0 truncate text-sm text-muted-foreground lg:block\">\n {makeSurfaceDefinition(resolvedRoute.surface).description}\n </p>\n </div>\n )}\n <div ref={setNavContainer} data-slot=\"explorer-nav\" className=\"contents\" />\n </>\n ),\n }\n : {})}\n collapsedStorageKey={SIDEBAR_COLLAPSED_KEY}\n // Results are wide tables, not prose — let them use the whole workspace\n // instead of the centred reading column.\n contentWidth=\"full\"\n contentClassName=\"p-6\"\n >\n {resolvedRoute.kind === \"surface\" ? (\n resolvedRoute.surface ? (\n <OperationCatalog\n // Filter state is seeded from the URL when the catalog mounts and\n // written back to it, so one instance spanning two surfaces would\n // carry the first surface's filters onto the second's URL. Keying\n // on the surface makes each one start from its own query string.\n key={resolvedRoute.surface.key}\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(resultRenderer ? { resultRenderer } : {})}\n {...(paginationMode ? { paginationMode } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"entity\" ? (\n resolvedRoute.surface ? (\n <OperationEntityPage\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n backHref={withBasePath(basePath, `/${resolvedRoute.surface.key}`)}\n backLabel={`Back to ${resolvedRoute.surface.title}`}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(entityDetailBodyRenderer ? { entityDetailBodyRenderer } : {})}\n {...(entityDetailHeaderRenderer ? { entityDetailHeaderRenderer } : {})}\n {...(resolvedRoute.id ? { id: resolvedRoute.id } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"command\" ? (\n <OperationCommandPage\n client={client}\n backHref={withBasePath(basePath, \"/explorer\")}\n backLabel=\"Back to API Explorer\"\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n initialValues={commandInitialValues}\n autoRun={true}\n {...(resolvedRoute.operationId ? { operationId: resolvedRoute.operationId } : {})}\n />\n ) : resolvedRoute.kind === \"explorer\" ? (\n <OperationCatalog\n definition={explorerDefinition}\n entities={[]}\n allOperations\n client={client}\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n />\n ) : defaultSurface == null ? (\n <div className=\"text-sm text-muted-foreground\">Loading surfaces…</div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">Redirecting…</div>\n )}\n </AppShell>\n );\n\n return <ExplorerNavContext.Provider value={navSlot}>{content}</ExplorerNavContext.Provider>;\n}\n\ntype ExplorerRoute =\n | { kind: \"root\" }\n | { kind: \"explorer\" }\n | { kind: \"command\"; operationId?: string }\n | { kind: \"surface\"; surfaceKey: string; surface?: ClickySurface }\n | {\n kind: \"entity\";\n surfaceKey: string;\n surface?: ClickySurface;\n id?: string;\n };\n\nfunction parseExplorerRoute(pathname: string): ExplorerRoute {\n const normalized = trimTrailingSlash(pathname) || \"/\";\n if (normalized === \"/\") {\n return { kind: \"root\" };\n }\n\n const segments = normalized.replace(/^\\/+/, \"\").split(\"/\");\n if (segments[0] === \"explorer\") {\n return { kind: \"explorer\" };\n }\n if (segments[0] === \"commands\") {\n return segments[1] ? { kind: \"command\", operationId: segments[1] } : { kind: \"command\" };\n }\n const surfaceKey = segments[0] ?? \"\";\n if (segments[1]) {\n return {\n kind: \"entity\",\n surfaceKey,\n id: decodeURIComponent(segments[1]),\n };\n }\n return { kind: \"surface\", surfaceKey };\n}\n\nfunction resolveRouteSurface(route: ExplorerRoute, surfaces: ClickySurface[]): ExplorerRoute {\n if (route.kind !== \"surface\" && route.kind !== \"entity\") {\n return route;\n }\n const surface = surfaces.find((item) => item.key === route.surfaceKey);\n return surface ? { ...route, surface } : route;\n}\n\nfunction isSurfaceRouteActive(route: ExplorerRoute, surfaceKey: string) {\n return (\n (route.kind === \"surface\" && route.surfaceKey === surfaceKey) ||\n (route.kind === \"entity\" && route.surfaceKey === surfaceKey)\n );\n}\n\nfunction stripBasePath(pathname: string, basePath: string) {\n const base = trimTrailingSlash(basePath);\n if (!base || base === \"/\") {\n return trimTrailingSlash(pathname) || \"/\";\n }\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return trimTrailingSlash(pathname.slice(base.length)) || \"/\";\n }\n return trimTrailingSlash(pathname) || \"/\";\n}\n\nfunction UnknownSurface({ surfaceKey }: { surfaceKey: string }) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n Unknown surface: <code>{surfaceKey}</code>\n </div>\n );\n}\n\nconst LEGACY_AUTORUN_QUERY_PARAM = \"__autoRun\";\n\ntype SurfaceGroup = {\n parent: string; // the slug; \"\" for surfaces that declared no parent\n label: string; // display name in the sidebar\n surfaces: ClickySurface[];\n};\n\n// groupSurfacesByParent buckets surfaces by their ClickySurface.parent slug,\n// preserving the order surfaces appeared in the spec. Surfaces with no parent\n// fall into a trailing \"\" bucket labelled \"Other\".\nfunction groupSurfacesByParent(surfaces: ClickySurface[]): SurfaceGroup[] {\n const order: string[] = [];\n const buckets = new Map<string, ClickySurface[]>();\n for (const surface of surfaces) {\n const parent = surface.parent ?? \"\";\n if (!buckets.has(parent)) {\n buckets.set(parent, []);\n order.push(parent);\n }\n buckets.get(parent)!.push(surface);\n }\n return order.map((parent) => ({\n parent,\n label: parent === \"\" ? \"Other\" : titleCase(parent),\n surfaces: buckets.get(parent)!,\n }));\n}\n\nfunction titleCase(value: string): string {\n if (!value) return value;\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction readCommandQueryParams(): { initialValues: Record<string, string>; autoRun: boolean } {\n if (typeof window === \"undefined\") {\n return { initialValues: {}, autoRun: false };\n }\n const search = new URLSearchParams(window.location.search);\n const initialValues: Record<string, string> = {};\n let autoRun = false;\n const positional: string[] = [];\n\n for (const [key, value] of search.entries()) {\n if (key === AUTORUN_QUERY_PARAM || key === LEGACY_AUTORUN_QUERY_PARAM) {\n autoRun = value === \"1\" || value === \"true\";\n continue;\n }\n if (key.startsWith(ARG_QUERY_PREFIX)) {\n const index = Number.parseInt(key.slice(ARG_QUERY_PREFIX.length), 10);\n if (Number.isFinite(index) && index >= 0) {\n positional[index] = value;\n }\n continue;\n }\n initialValues[key] = value;\n }\n\n const packedArgs = positional.filter((value) => value !== undefined).join(\" \");\n if (packedArgs !== \"\") {\n initialValues.args = packedArgs;\n }\n\n return { initialValues, autoRun };\n}\n"],"mappings":"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"}
@@ -48,6 +48,15 @@ export type EntityExplorerAppProps = {
48
48
  entityDetailBodyRenderer?: EntityDetailBodyRenderer;
49
49
  /** Host override for a loaded entity's "{title}: {id}" heading. */
50
50
  entityDetailHeaderRenderer?: EntityDetailHeaderRenderer;
51
+ /** Compact host controls (e.g. an edit icon) shown right after a surface's title or breadcrumb in the top bar. */
52
+ surfaceTitleActions?: (surfaceKey: string) => ReactNode;
53
+ /**
54
+ * Per-surface paging presentation, forwarded to OperationCatalog's
55
+ * `paginationMode`. Return undefined to keep automatic selection (a cursor
56
+ * walk whenever the list declares a cursor); return "paged" for a surface
57
+ * whose readers need prev/next offset controls.
58
+ */
59
+ surfacePaginationMode?: (surfaceKey: string) => "paged" | "infinite" | undefined;
51
60
  };
52
- export declare function EntityExplorerApp({ client, basePath, showApiExplorer, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer, }: EntityExplorerAppProps): import("react").JSX.Element;
61
+ export declare function EntityExplorerApp({ client, basePath, showApiExplorer, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer, surfaceTitleActions, surfacePaginationMode, }: EntityExplorerAppProps): import("react").JSX.Element;
53
62
  //# sourceMappingURL=EntityExplorerApp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityExplorerApp.d.ts","sourceRoot":"","sources":["../../src/rpc/EntityExplorerApp.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAaxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC;IAClE;;;;OAIG;IACH,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,mEAAmE;IACnE,0BAA0B,CAAC,EAAE,0BAA0B,CAAC;CACzD,CAAC;AAKF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAa,EACb,eAAsB,EACtB,OAAO,EACP,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,wBAAwB,EACxB,0BAA0B,GAC3B,EAAE,sBAAsB,+BA6LxB"}
1
+ {"version":3,"file":"EntityExplorerApp.d.ts","sourceRoot":"","sources":["../../src/rpc/EntityExplorerApp.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlF,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAaxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC;IAClE;;;;OAIG;IACH,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,mEAAmE;IACnE,0BAA0B,CAAC,EAAE,0BAA0B,CAAC;IACxD,kHAAkH;IAClH,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,SAAS,CAAC;IACxD;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;CAClF,CAAC;AAKF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAa,EACb,eAAsB,EACtB,OAAO,EACP,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,GACtB,EAAE,sBAAsB,+BA2NxB"}
@@ -4,15 +4,16 @@ import { AppShell } from "../layout/AppShell.js";
4
4
  import { useOperations } from "./useOperations.js";
5
5
  import { getClickySurfaces, makeSurfaceDefinition } from "./clickyMetadata.js";
6
6
  import { OperationCatalog } from "./OperationCatalog.js";
7
+ import { ExplorerNavContext } from "./explorer-nav-context.js";
7
8
  import { ARG_QUERY_PREFIX, buildCommandHref, trimTrailingSlash, withBasePath } from "./commandHref.js";
8
9
  import { OperationCommandPage } from "./OperationCommandPage.js";
9
10
  import { OperationEntityPage } from "./OperationEntityPage.js";
10
11
  import { surfaceNavSection } from "./surfaceNav.js";
11
- import { useEffect, useMemo } from "react";
12
- import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { useCallback, useEffect, useMemo, useState } from "react";
13
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
14
  // src/rpc/EntityExplorerApp.tsx
14
15
  var SIDEBAR_COLLAPSED_KEY = "clicky-ui:sidebar:collapsed";
15
- function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer }) {
16
+ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, actions, formExtensions, formActions, surfaceActionLabels, resultRenderer, entityDetailBodyRenderer, entityDetailHeaderRenderer, surfaceTitleActions, surfacePaginationMode }) {
16
17
  const formPre = formExtensions?.pre;
17
18
  const formPost = formExtensions?.post;
18
19
  const { pathname, renderLink, navigate } = useRouter();
@@ -64,6 +65,26 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
64
65
  showApiExplorer,
65
66
  explorerDefinition
66
67
  ]);
68
+ const [navContainer, setNavContainer] = useState(null);
69
+ const [navClaims, setNavClaims] = useState(0);
70
+ const claimNav = useCallback(() => {
71
+ setNavClaims((count) => count + 1);
72
+ return () => setNavClaims((count) => count - 1);
73
+ }, []);
74
+ const surface = resolvedRoute.kind === "surface" ? resolvedRoute.surface : void 0;
75
+ const titleActions = surface ? surfaceTitleActions?.(surface.key) : void 0;
76
+ const paginationMode = surface ? surfacePaginationMode?.(surface.key) : void 0;
77
+ const navSlot = useMemo(() => ({
78
+ container: surface ? navContainer : null,
79
+ title: surface?.title ?? "",
80
+ titleActions,
81
+ claim: claimNav
82
+ }), [
83
+ claimNav,
84
+ navContainer,
85
+ surface,
86
+ titleActions
87
+ ]);
67
88
  useEffect(() => {
68
89
  if (defaultSurface == null || relativePath !== "/") return;
69
90
  navigate(withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });
@@ -73,7 +94,7 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
73
94
  relativePath,
74
95
  navigate
75
96
  ]);
76
- return /* @__PURE__ */ jsx(AppShell, {
97
+ const content = /* @__PURE__ */ jsx(AppShell, {
77
98
  brand: /* @__PURE__ */ jsxs("div", {
78
99
  className: "min-w-0",
79
100
  children: [/* @__PURE__ */ jsx("div", {
@@ -87,16 +108,27 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
87
108
  navSections,
88
109
  sidebarFooter: /* @__PURE__ */ jsx(ThemeSwitcher, {}),
89
110
  ...actions ? { actions } : {},
90
- ...resolvedRoute.kind === "surface" && resolvedRoute.surface ? { nav: /* @__PURE__ */ jsxs("div", {
111
+ ...resolvedRoute.kind === "surface" && resolvedRoute.surface ? { nav: /* @__PURE__ */ jsxs(Fragment$1, { children: [navClaims === 0 && /* @__PURE__ */ jsxs("div", {
91
112
  className: "flex min-w-0 flex-1 items-baseline gap-density-2",
92
- children: [/* @__PURE__ */ jsx("h1", {
93
- className: "min-w-0 truncate text-base font-semibold tracking-tight",
94
- children: resolvedRoute.surface.title
95
- }), /* @__PURE__ */ jsx("p", {
96
- className: "hidden min-w-0 truncate text-sm text-muted-foreground lg:block",
97
- children: makeSurfaceDefinition(resolvedRoute.surface).description
98
- })]
99
- }) } : {},
113
+ children: [
114
+ /* @__PURE__ */ jsx("h1", {
115
+ className: "min-w-0 truncate text-base font-semibold tracking-tight",
116
+ children: resolvedRoute.surface.title
117
+ }),
118
+ titleActions ? /* @__PURE__ */ jsx("div", {
119
+ className: "flex shrink-0 items-center self-center",
120
+ children: titleActions
121
+ }) : null,
122
+ /* @__PURE__ */ jsx("p", {
123
+ className: "hidden min-w-0 truncate text-sm text-muted-foreground lg:block",
124
+ children: makeSurfaceDefinition(resolvedRoute.surface).description
125
+ })
126
+ ]
127
+ }), /* @__PURE__ */ jsx("div", {
128
+ ref: setNavContainer,
129
+ "data-slot": "explorer-nav",
130
+ className: "contents"
131
+ })] }) } : {},
100
132
  collapsedStorageKey: SIDEBAR_COLLAPSED_KEY,
101
133
  contentWidth: "full",
102
134
  contentClassName: "p-6",
@@ -111,7 +143,8 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
111
143
  ...formPost ? { formPost } : {},
112
144
  ...formActions ? { formActions } : {},
113
145
  ...surfaceActionLabels?.[resolvedRoute.surface.key] ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] } : {},
114
- ...resultRenderer ? { resultRenderer } : {}
146
+ ...resultRenderer ? { resultRenderer } : {},
147
+ ...paginationMode ? { paginationMode } : {}
115
148
  }, resolvedRoute.surface.key) : /* @__PURE__ */ jsx(UnknownSurface, { surfaceKey: resolvedRoute.surfaceKey }) : resolvedRoute.kind === "entity" ? resolvedRoute.surface ? /* @__PURE__ */ jsx(OperationEntityPage, {
116
149
  definition: makeSurfaceDefinition(resolvedRoute.surface),
117
150
  entities: [resolvedRoute.surface.entity],
@@ -152,6 +185,10 @@ function EntityExplorerApp({ client, basePath = "", showApiExplorer = true, acti
152
185
  children: "Redirecting…"
153
186
  })
154
187
  });
188
+ return /* @__PURE__ */ jsx(ExplorerNavContext.Provider, {
189
+ value: navSlot,
190
+ children: content
191
+ });
155
192
  }
156
193
  function parseExplorerRoute(pathname) {
157
194
  const normalized = trimTrailingSlash(pathname) || "/";
@@ -1 +1 @@
1
- {"version":3,"file":"EntityExplorerApp.js","names":["_jsx"],"sources":["../../src/rpc/EntityExplorerApp.tsx"],"sourcesContent":["import { useEffect, useMemo, type ReactNode } from \"react\";\nimport { ThemeSwitcher } from \"../components/theme-switcher\";\nimport type { PreExtension, PostExtension } from \"../components/json-schema-form-types\";\nimport type { ClickyCommandRuntime } from \"../data/Clicky\";\nimport { AppShell, type AppShellNavSection } from \"../layout/AppShell\";\nimport { getClickySurfaces, makeSurfaceDefinition } from \"./clickyMetadata\";\nimport {\n ARG_QUERY_PREFIX,\n AUTORUN_QUERY_PARAM,\n buildCommandHref,\n trimTrailingSlash,\n withBasePath,\n} from \"./commandHref\";\nimport { useRouter } from \"./router\";\nimport { OperationCatalog } from \"./OperationCatalog\";\nimport type { ResultRenderer } from \"./OperationResultView\";\nimport { OperationCommandPage } from \"./OperationCommandPage\";\nimport {\n OperationEntityPage,\n type EntityDetailBodyRenderer,\n type EntityDetailHeaderRenderer,\n} from \"./OperationEntityPage\";\nimport type { FormActionsRenderer } from \"./SchemaActionForm\";\nimport { surfaceNavSection } from \"./surfaceNav\";\nimport type { ClickySurface } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\nimport { useOperations } from \"./useOperations\";\n\nexport type SurfaceActionLabels = Record<string, Record<string, string>>;\n\nexport type EntityExplorerAppProps = {\n client: OperationsApiClient;\n basePath?: string;\n showApiExplorer?: boolean;\n /** Right-aligned top-bar cluster forwarded to AppShell's actions slot. */\n actions?: ReactNode;\n /**\n * Custom JsonSchemaForm field extensions applied to every create/edit form the\n * explorer renders — e.g. a SecretKeySelector widget keyed on an\n * `x-clicky-component` schema hint. `pre` transforms a resolved control; `post`\n * can replace the rendered value node with a custom component.\n */\n formExtensions?: { pre?: PreExtension[]; post?: PostExtension[] };\n /**\n * Optional extra footer actions added to every create/edit form (e.g. a\n * connection \"Test\" split-button). The renderer receives the live form value\n * and the action it submits to, so it can scope itself to a given entity.\n */\n formActions?: FormActionsRenderer;\n /** Per-surface labels keyed by x-clicky verb/actionName. */\n surfaceActionLabels?: SurfaceActionLabels;\n /**\n * @deprecated Superseded by per-surface icons emitted by the backend\n * (x-clicky-icon → ClickySurface.icon). AppShell section labels are plain\n * strings, so this group-header icon map is no longer rendered. Kept for\n * source-compatibility only.\n */\n surfaceIcons?: Record<string, ReactNode>;\n /**\n * Optional host override for the result surface. Called where an entity's\n * result table renders, with the current surface key, the raw response, and\n * the default OperationResultView element. Return `defaultView` to keep the\n * standard table, or a custom node (e.g. a LogsTable for trace/log profiles\n * flagged via x-clicky-render). Filtering/sorting policy is the host's choice.\n */\n resultRenderer?: ResultRenderer;\n /** Host override for a loaded entity's body; heading and actions remain. */\n entityDetailBodyRenderer?: EntityDetailBodyRenderer;\n /** Host override for a loaded entity's \"{title}: {id}\" heading. */\n entityDetailHeaderRenderer?: EntityDetailHeaderRenderer;\n};\n\n// SIDEBAR_COLLAPSED_KEY persists the AppShell rail collapsed flag across reloads.\nconst SIDEBAR_COLLAPSED_KEY = \"clicky-ui:sidebar:collapsed\";\n\nexport function EntityExplorerApp({\n client,\n basePath = \"\",\n showApiExplorer = true,\n actions,\n formExtensions,\n formActions,\n surfaceActionLabels,\n resultRenderer,\n entityDetailBodyRenderer,\n entityDetailHeaderRenderer,\n}: EntityExplorerAppProps) {\n const formPre = formExtensions?.pre;\n const formPost = formExtensions?.post;\n const { pathname, renderLink, navigate } = useRouter();\n const { spec } = useOperations(client);\n const surfaces = useMemo(() => getClickySurfaces(spec), [spec]);\n const surfaceGroups = useMemo(() => groupSurfacesByParent(surfaces), [surfaces]);\n const defaultSurface = useMemo(\n () => surfaces.find((surface) => !surface.admin) ?? surfaces[0],\n [surfaces],\n );\n const relativePath = useMemo(() => stripBasePath(pathname, basePath), [basePath, pathname]);\n const route = useMemo(() => parseExplorerRoute(relativePath), [relativePath]);\n const resolvedRoute = useMemo(() => resolveRouteSurface(route, surfaces), [route, surfaces]);\n const commandRuntime = useMemo<ClickyCommandRuntime>(\n () => ({\n client,\n hrefForCommand: (resolved) => buildCommandHref(basePath, resolved),\n onNavigate: (resolved) => {\n const href = buildCommandHref(basePath, resolved);\n if (!href) return;\n navigate(href);\n },\n }),\n [basePath, client, navigate],\n );\n const { initialValues: commandInitialValues } = useMemo(\n () =>\n resolvedRoute.kind === \"command\"\n ? readCommandQueryParams()\n : { initialValues: {}, autoRun: false },\n [resolvedRoute.kind, pathname],\n );\n const explorerDefinition = useMemo(\n () => ({\n key: \"explorer\",\n title: \"API Explorer\",\n description: \"Every operation exposed by the current OpenAPI spec.\",\n }),\n [],\n );\n const navSections = useMemo<AppShellNavSection[]>(() => {\n const sections: AppShellNavSection[] = surfaceGroups.map((group) =>\n surfaceNavSection(group.label, group.surfaces, {\n isActive: (surface) => isSurfaceRouteActive(resolvedRoute, surface.key),\n hrefFor: (surface) => withBasePath(basePath, `/${surface.key}`),\n }),\n );\n if (showApiExplorer) {\n sections.push({\n items: [\n {\n key: explorerDefinition.key,\n label: explorerDefinition.title,\n active: resolvedRoute.kind === \"explorer\" || resolvedRoute.kind === \"command\",\n to: withBasePath(basePath, \"/explorer\"),\n },\n ],\n });\n }\n return sections;\n }, [surfaceGroups, basePath, resolvedRoute, showApiExplorer, explorerDefinition]);\n\n useEffect(() => {\n if (defaultSurface == null || relativePath !== \"/\") return;\n navigate(withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });\n }, [basePath, defaultSurface, relativePath, navigate]);\n\n const content = (\n <AppShell\n brand={\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold\">\n {spec?.info.title ?? \"Clicky Explorer\"}\n </div>\n <div className=\"truncate text-xs text-sidebar-foreground/60\">\n Metadata-driven entity explorer\n </div>\n </div>\n }\n navSections={navSections}\n sidebarFooter={<ThemeSwitcher />}\n {...(actions ? { actions } : {})}\n // The page header belongs to the host, not to OperationCatalog — the\n // catalog renders only its results. OperationEntityPage still titles\n // itself, so this covers the collection route only.\n //\n // It rides in the top bar rather than a bodyHeader row: next to the rail\n // that bar is otherwise empty, and the row it replaces cost the result\n // table ~72px of the viewport it needs to keep its sticky head and\n // pagination footer on screen without the page itself scrolling.\n {...(resolvedRoute.kind === \"surface\" && resolvedRoute.surface\n ? {\n nav: (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-density-2\">\n <h1 className=\"min-w-0 truncate text-base font-semibold tracking-tight\">\n {resolvedRoute.surface.title}\n </h1>\n <p className=\"hidden min-w-0 truncate text-sm text-muted-foreground lg:block\">\n {makeSurfaceDefinition(resolvedRoute.surface).description}\n </p>\n </div>\n ),\n }\n : {})}\n collapsedStorageKey={SIDEBAR_COLLAPSED_KEY}\n // Results are wide tables, not prose — let them use the whole workspace\n // instead of the centred reading column.\n contentWidth=\"full\"\n contentClassName=\"p-6\"\n >\n {resolvedRoute.kind === \"surface\" ? (\n resolvedRoute.surface ? (\n <OperationCatalog\n // Filter state is seeded from the URL when the catalog mounts and\n // written back to it, so one instance spanning two surfaces would\n // carry the first surface's filters onto the second's URL. Keying\n // on the surface makes each one start from its own query string.\n key={resolvedRoute.surface.key}\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(resultRenderer ? { resultRenderer } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"entity\" ? (\n resolvedRoute.surface ? (\n <OperationEntityPage\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n backHref={withBasePath(basePath, `/${resolvedRoute.surface.key}`)}\n backLabel={`Back to ${resolvedRoute.surface.title}`}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(entityDetailBodyRenderer ? { entityDetailBodyRenderer } : {})}\n {...(entityDetailHeaderRenderer ? { entityDetailHeaderRenderer } : {})}\n {...(resolvedRoute.id ? { id: resolvedRoute.id } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"command\" ? (\n <OperationCommandPage\n client={client}\n backHref={withBasePath(basePath, \"/explorer\")}\n backLabel=\"Back to API Explorer\"\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n initialValues={commandInitialValues}\n autoRun={true}\n {...(resolvedRoute.operationId ? { operationId: resolvedRoute.operationId } : {})}\n />\n ) : resolvedRoute.kind === \"explorer\" ? (\n <OperationCatalog\n definition={explorerDefinition}\n entities={[]}\n allOperations\n client={client}\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n />\n ) : defaultSurface == null ? (\n <div className=\"text-sm text-muted-foreground\">Loading surfaces…</div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">Redirecting…</div>\n )}\n </AppShell>\n );\n\n return content;\n}\n\ntype ExplorerRoute =\n | { kind: \"root\" }\n | { kind: \"explorer\" }\n | { kind: \"command\"; operationId?: string }\n | { kind: \"surface\"; surfaceKey: string; surface?: ClickySurface }\n | {\n kind: \"entity\";\n surfaceKey: string;\n surface?: ClickySurface;\n id?: string;\n };\n\nfunction parseExplorerRoute(pathname: string): ExplorerRoute {\n const normalized = trimTrailingSlash(pathname) || \"/\";\n if (normalized === \"/\") {\n return { kind: \"root\" };\n }\n\n const segments = normalized.replace(/^\\/+/, \"\").split(\"/\");\n if (segments[0] === \"explorer\") {\n return { kind: \"explorer\" };\n }\n if (segments[0] === \"commands\") {\n return segments[1] ? { kind: \"command\", operationId: segments[1] } : { kind: \"command\" };\n }\n const surfaceKey = segments[0] ?? \"\";\n if (segments[1]) {\n return {\n kind: \"entity\",\n surfaceKey,\n id: decodeURIComponent(segments[1]),\n };\n }\n return { kind: \"surface\", surfaceKey };\n}\n\nfunction resolveRouteSurface(route: ExplorerRoute, surfaces: ClickySurface[]): ExplorerRoute {\n if (route.kind !== \"surface\" && route.kind !== \"entity\") {\n return route;\n }\n const surface = surfaces.find((item) => item.key === route.surfaceKey);\n return surface ? { ...route, surface } : route;\n}\n\nfunction isSurfaceRouteActive(route: ExplorerRoute, surfaceKey: string) {\n return (\n (route.kind === \"surface\" && route.surfaceKey === surfaceKey) ||\n (route.kind === \"entity\" && route.surfaceKey === surfaceKey)\n );\n}\n\nfunction stripBasePath(pathname: string, basePath: string) {\n const base = trimTrailingSlash(basePath);\n if (!base || base === \"/\") {\n return trimTrailingSlash(pathname) || \"/\";\n }\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return trimTrailingSlash(pathname.slice(base.length)) || \"/\";\n }\n return trimTrailingSlash(pathname) || \"/\";\n}\n\nfunction UnknownSurface({ surfaceKey }: { surfaceKey: string }) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n Unknown surface: <code>{surfaceKey}</code>\n </div>\n );\n}\n\nconst LEGACY_AUTORUN_QUERY_PARAM = \"__autoRun\";\n\ntype SurfaceGroup = {\n parent: string; // the slug; \"\" for surfaces that declared no parent\n label: string; // display name in the sidebar\n surfaces: ClickySurface[];\n};\n\n// groupSurfacesByParent buckets surfaces by their ClickySurface.parent slug,\n// preserving the order surfaces appeared in the spec. Surfaces with no parent\n// fall into a trailing \"\" bucket labelled \"Other\".\nfunction groupSurfacesByParent(surfaces: ClickySurface[]): SurfaceGroup[] {\n const order: string[] = [];\n const buckets = new Map<string, ClickySurface[]>();\n for (const surface of surfaces) {\n const parent = surface.parent ?? \"\";\n if (!buckets.has(parent)) {\n buckets.set(parent, []);\n order.push(parent);\n }\n buckets.get(parent)!.push(surface);\n }\n return order.map((parent) => ({\n parent,\n label: parent === \"\" ? \"Other\" : titleCase(parent),\n surfaces: buckets.get(parent)!,\n }));\n}\n\nfunction titleCase(value: string): string {\n if (!value) return value;\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction readCommandQueryParams(): { initialValues: Record<string, string>; autoRun: boolean } {\n if (typeof window === \"undefined\") {\n return { initialValues: {}, autoRun: false };\n }\n const search = new URLSearchParams(window.location.search);\n const initialValues: Record<string, string> = {};\n let autoRun = false;\n const positional: string[] = [];\n\n for (const [key, value] of search.entries()) {\n if (key === AUTORUN_QUERY_PARAM || key === LEGACY_AUTORUN_QUERY_PARAM) {\n autoRun = value === \"1\" || value === \"true\";\n continue;\n }\n if (key.startsWith(ARG_QUERY_PREFIX)) {\n const index = Number.parseInt(key.slice(ARG_QUERY_PREFIX.length), 10);\n if (Number.isFinite(index) && index >= 0) {\n positional[index] = value;\n }\n continue;\n }\n initialValues[key] = value;\n }\n\n const packedArgs = positional.filter((value) => value !== undefined).join(\" \");\n if (packedArgs !== \"\") {\n initialValues.args = packedArgs;\n }\n\n return { initialValues, autoRun };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EntityExplorerApp.js","names":["_Fragment"],"sources":["../../src/rpc/EntityExplorerApp.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { ExplorerNavContext, type ExplorerNavSlot } from \"./explorer-nav-context\";\nimport { ThemeSwitcher } from \"../components/theme-switcher\";\nimport type { PreExtension, PostExtension } from \"../components/json-schema-form-types\";\nimport type { ClickyCommandRuntime } from \"../data/Clicky\";\nimport { AppShell, type AppShellNavSection } from \"../layout/AppShell\";\nimport { getClickySurfaces, makeSurfaceDefinition } from \"./clickyMetadata\";\nimport {\n ARG_QUERY_PREFIX,\n AUTORUN_QUERY_PARAM,\n buildCommandHref,\n trimTrailingSlash,\n withBasePath,\n} from \"./commandHref\";\nimport { useRouter } from \"./router\";\nimport { OperationCatalog } from \"./OperationCatalog\";\nimport type { ResultRenderer } from \"./OperationResultView\";\nimport { OperationCommandPage } from \"./OperationCommandPage\";\nimport {\n OperationEntityPage,\n type EntityDetailBodyRenderer,\n type EntityDetailHeaderRenderer,\n} from \"./OperationEntityPage\";\nimport type { FormActionsRenderer } from \"./SchemaActionForm\";\nimport { surfaceNavSection } from \"./surfaceNav\";\nimport type { ClickySurface } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\nimport { useOperations } from \"./useOperations\";\n\nexport type SurfaceActionLabels = Record<string, Record<string, string>>;\n\nexport type EntityExplorerAppProps = {\n client: OperationsApiClient;\n basePath?: string;\n showApiExplorer?: boolean;\n /** Right-aligned top-bar cluster forwarded to AppShell's actions slot. */\n actions?: ReactNode;\n /**\n * Custom JsonSchemaForm field extensions applied to every create/edit form the\n * explorer renders — e.g. a SecretKeySelector widget keyed on an\n * `x-clicky-component` schema hint. `pre` transforms a resolved control; `post`\n * can replace the rendered value node with a custom component.\n */\n formExtensions?: { pre?: PreExtension[]; post?: PostExtension[] };\n /**\n * Optional extra footer actions added to every create/edit form (e.g. a\n * connection \"Test\" split-button). The renderer receives the live form value\n * and the action it submits to, so it can scope itself to a given entity.\n */\n formActions?: FormActionsRenderer;\n /** Per-surface labels keyed by x-clicky verb/actionName. */\n surfaceActionLabels?: SurfaceActionLabels;\n /**\n * @deprecated Superseded by per-surface icons emitted by the backend\n * (x-clicky-icon → ClickySurface.icon). AppShell section labels are plain\n * strings, so this group-header icon map is no longer rendered. Kept for\n * source-compatibility only.\n */\n surfaceIcons?: Record<string, ReactNode>;\n /**\n * Optional host override for the result surface. Called where an entity's\n * result table renders, with the current surface key, the raw response, and\n * the default OperationResultView element. Return `defaultView` to keep the\n * standard table, or a custom node (e.g. a LogsTable for trace/log profiles\n * flagged via x-clicky-render). Filtering/sorting policy is the host's choice.\n */\n resultRenderer?: ResultRenderer;\n /** Host override for a loaded entity's body; heading and actions remain. */\n entityDetailBodyRenderer?: EntityDetailBodyRenderer;\n /** Host override for a loaded entity's \"{title}: {id}\" heading. */\n entityDetailHeaderRenderer?: EntityDetailHeaderRenderer;\n /** Compact host controls (e.g. an edit icon) shown right after a surface's title or breadcrumb in the top bar. */\n surfaceTitleActions?: (surfaceKey: string) => ReactNode;\n /**\n * Per-surface paging presentation, forwarded to OperationCatalog's\n * `paginationMode`. Return undefined to keep automatic selection (a cursor\n * walk whenever the list declares a cursor); return \"paged\" for a surface\n * whose readers need prev/next offset controls.\n */\n surfacePaginationMode?: (surfaceKey: string) => \"paged\" | \"infinite\" | undefined;\n};\n\n// SIDEBAR_COLLAPSED_KEY persists the AppShell rail collapsed flag across reloads.\nconst SIDEBAR_COLLAPSED_KEY = \"clicky-ui:sidebar:collapsed\";\n\nexport function EntityExplorerApp({\n client,\n basePath = \"\",\n showApiExplorer = true,\n actions,\n formExtensions,\n formActions,\n surfaceActionLabels,\n resultRenderer,\n entityDetailBodyRenderer,\n entityDetailHeaderRenderer,\n surfaceTitleActions,\n surfacePaginationMode,\n}: EntityExplorerAppProps) {\n const formPre = formExtensions?.pre;\n const formPost = formExtensions?.post;\n const { pathname, renderLink, navigate } = useRouter();\n const { spec } = useOperations(client);\n const surfaces = useMemo(() => getClickySurfaces(spec), [spec]);\n const surfaceGroups = useMemo(() => groupSurfacesByParent(surfaces), [surfaces]);\n const defaultSurface = useMemo(\n () => surfaces.find((surface) => !surface.admin) ?? surfaces[0],\n [surfaces],\n );\n const relativePath = useMemo(() => stripBasePath(pathname, basePath), [basePath, pathname]);\n const route = useMemo(() => parseExplorerRoute(relativePath), [relativePath]);\n const resolvedRoute = useMemo(() => resolveRouteSurface(route, surfaces), [route, surfaces]);\n const commandRuntime = useMemo<ClickyCommandRuntime>(\n () => ({\n client,\n hrefForCommand: (resolved) => buildCommandHref(basePath, resolved),\n onNavigate: (resolved) => {\n const href = buildCommandHref(basePath, resolved);\n if (!href) return;\n navigate(href);\n },\n }),\n [basePath, client, navigate],\n );\n const { initialValues: commandInitialValues } = useMemo(\n () =>\n resolvedRoute.kind === \"command\"\n ? readCommandQueryParams()\n : { initialValues: {}, autoRun: false },\n [resolvedRoute.kind, pathname],\n );\n const explorerDefinition = useMemo(\n () => ({\n key: \"explorer\",\n title: \"API Explorer\",\n description: \"Every operation exposed by the current OpenAPI spec.\",\n }),\n [],\n );\n const navSections = useMemo<AppShellNavSection[]>(() => {\n const sections: AppShellNavSection[] = surfaceGroups.map((group) =>\n surfaceNavSection(group.label, group.surfaces, {\n isActive: (surface) => isSurfaceRouteActive(resolvedRoute, surface.key),\n hrefFor: (surface) => withBasePath(basePath, `/${surface.key}`),\n }),\n );\n if (showApiExplorer) {\n sections.push({\n items: [\n {\n key: explorerDefinition.key,\n label: explorerDefinition.title,\n active: resolvedRoute.kind === \"explorer\" || resolvedRoute.kind === \"command\",\n to: withBasePath(basePath, \"/explorer\"),\n },\n ],\n });\n }\n return sections;\n }, [surfaceGroups, basePath, resolvedRoute, showApiExplorer, explorerDefinition]);\n\n const [navContainer, setNavContainer] = useState<HTMLElement | null>(null);\n const [navClaims, setNavClaims] = useState(0);\n const claimNav = useCallback(() => {\n setNavClaims((count) => count + 1);\n return () => setNavClaims((count) => count - 1);\n }, []);\n const surface = resolvedRoute.kind === \"surface\" ? resolvedRoute.surface : undefined;\n const titleActions = surface ? surfaceTitleActions?.(surface.key) : undefined;\n const paginationMode = surface ? surfacePaginationMode?.(surface.key) : undefined;\n // The container only exists while a surface is resolved, so a portal\n // mounted before the spec loads renders nothing rather than a bare crumb.\n const navSlot = useMemo<ExplorerNavSlot>(\n () => ({\n container: surface ? navContainer : null,\n title: surface?.title ?? \"\",\n titleActions,\n claim: claimNav,\n }),\n [claimNav, navContainer, surface, titleActions],\n );\n\n useEffect(() => {\n if (defaultSurface == null || relativePath !== \"/\") return;\n navigate(withBasePath(basePath, `/${defaultSurface.key}`), { replace: true });\n }, [basePath, defaultSurface, relativePath, navigate]);\n\n const content = (\n <AppShell\n brand={\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold\">\n {spec?.info.title ?? \"Clicky Explorer\"}\n </div>\n <div className=\"truncate text-xs text-sidebar-foreground/60\">\n Metadata-driven entity explorer\n </div>\n </div>\n }\n navSections={navSections}\n sidebarFooter={<ThemeSwitcher />}\n {...(actions ? { actions } : {})}\n // The page header belongs to the host, not to OperationCatalog — the\n // catalog renders only its results. OperationEntityPage still titles\n // itself, so this covers the collection route only.\n //\n // It rides in the top bar rather than a bodyHeader row: next to the rail\n // that bar is otherwise empty, and the row it replaces cost the result\n // table ~72px of the viewport it needs to keep its sticky head and\n // pagination footer on screen without the page itself scrolling.\n {...(resolvedRoute.kind === \"surface\" && resolvedRoute.surface\n ? {\n nav: (\n <>\n {navClaims === 0 && (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-density-2\">\n <h1 className=\"min-w-0 truncate text-base font-semibold tracking-tight\">\n {resolvedRoute.surface.title}\n </h1>\n {titleActions ? (\n <div className=\"flex shrink-0 items-center self-center\">{titleActions}</div>\n ) : null}\n <p className=\"hidden min-w-0 truncate text-sm text-muted-foreground lg:block\">\n {makeSurfaceDefinition(resolvedRoute.surface).description}\n </p>\n </div>\n )}\n <div ref={setNavContainer} data-slot=\"explorer-nav\" className=\"contents\" />\n </>\n ),\n }\n : {})}\n collapsedStorageKey={SIDEBAR_COLLAPSED_KEY}\n // Results are wide tables, not prose — let them use the whole workspace\n // instead of the centred reading column.\n contentWidth=\"full\"\n contentClassName=\"p-6\"\n >\n {resolvedRoute.kind === \"surface\" ? (\n resolvedRoute.surface ? (\n <OperationCatalog\n // Filter state is seeded from the URL when the catalog mounts and\n // written back to it, so one instance spanning two surfaces would\n // carry the first surface's filters onto the second's URL. Keying\n // on the surface makes each one start from its own query string.\n key={resolvedRoute.surface.key}\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(resultRenderer ? { resultRenderer } : {})}\n {...(paginationMode ? { paginationMode } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"entity\" ? (\n resolvedRoute.surface ? (\n <OperationEntityPage\n definition={makeSurfaceDefinition(resolvedRoute.surface)}\n entities={[resolvedRoute.surface.entity]}\n client={client}\n renderLink={renderLink}\n surfaceKey={resolvedRoute.surface.key}\n backHref={withBasePath(basePath, `/${resolvedRoute.surface.key}`)}\n backLabel={`Back to ${resolvedRoute.surface.title}`}\n commandRuntime={commandRuntime}\n {...(formPre ? { formPre } : {})}\n {...(formPost ? { formPost } : {})}\n {...(formActions ? { formActions } : {})}\n {...(surfaceActionLabels?.[resolvedRoute.surface.key]\n ? { actionLabels: surfaceActionLabels[resolvedRoute.surface.key] }\n : {})}\n {...(entityDetailBodyRenderer ? { entityDetailBodyRenderer } : {})}\n {...(entityDetailHeaderRenderer ? { entityDetailHeaderRenderer } : {})}\n {...(resolvedRoute.id ? { id: resolvedRoute.id } : {})}\n />\n ) : (\n <UnknownSurface surfaceKey={resolvedRoute.surfaceKey} />\n )\n ) : resolvedRoute.kind === \"command\" ? (\n <OperationCommandPage\n client={client}\n backHref={withBasePath(basePath, \"/explorer\")}\n backLabel=\"Back to API Explorer\"\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n initialValues={commandInitialValues}\n autoRun={true}\n {...(resolvedRoute.operationId ? { operationId: resolvedRoute.operationId } : {})}\n />\n ) : resolvedRoute.kind === \"explorer\" ? (\n <OperationCatalog\n definition={explorerDefinition}\n entities={[]}\n allOperations\n client={client}\n renderLink={renderLink}\n commandRuntime={commandRuntime}\n />\n ) : defaultSurface == null ? (\n <div className=\"text-sm text-muted-foreground\">Loading surfaces…</div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">Redirecting…</div>\n )}\n </AppShell>\n );\n\n return <ExplorerNavContext.Provider value={navSlot}>{content}</ExplorerNavContext.Provider>;\n}\n\ntype ExplorerRoute =\n | { kind: \"root\" }\n | { kind: \"explorer\" }\n | { kind: \"command\"; operationId?: string }\n | { kind: \"surface\"; surfaceKey: string; surface?: ClickySurface }\n | {\n kind: \"entity\";\n surfaceKey: string;\n surface?: ClickySurface;\n id?: string;\n };\n\nfunction parseExplorerRoute(pathname: string): ExplorerRoute {\n const normalized = trimTrailingSlash(pathname) || \"/\";\n if (normalized === \"/\") {\n return { kind: \"root\" };\n }\n\n const segments = normalized.replace(/^\\/+/, \"\").split(\"/\");\n if (segments[0] === \"explorer\") {\n return { kind: \"explorer\" };\n }\n if (segments[0] === \"commands\") {\n return segments[1] ? { kind: \"command\", operationId: segments[1] } : { kind: \"command\" };\n }\n const surfaceKey = segments[0] ?? \"\";\n if (segments[1]) {\n return {\n kind: \"entity\",\n surfaceKey,\n id: decodeURIComponent(segments[1]),\n };\n }\n return { kind: \"surface\", surfaceKey };\n}\n\nfunction resolveRouteSurface(route: ExplorerRoute, surfaces: ClickySurface[]): ExplorerRoute {\n if (route.kind !== \"surface\" && route.kind !== \"entity\") {\n return route;\n }\n const surface = surfaces.find((item) => item.key === route.surfaceKey);\n return surface ? { ...route, surface } : route;\n}\n\nfunction isSurfaceRouteActive(route: ExplorerRoute, surfaceKey: string) {\n return (\n (route.kind === \"surface\" && route.surfaceKey === surfaceKey) ||\n (route.kind === \"entity\" && route.surfaceKey === surfaceKey)\n );\n}\n\nfunction stripBasePath(pathname: string, basePath: string) {\n const base = trimTrailingSlash(basePath);\n if (!base || base === \"/\") {\n return trimTrailingSlash(pathname) || \"/\";\n }\n if (pathname === base) {\n return \"/\";\n }\n if (pathname.startsWith(`${base}/`)) {\n return trimTrailingSlash(pathname.slice(base.length)) || \"/\";\n }\n return trimTrailingSlash(pathname) || \"/\";\n}\n\nfunction UnknownSurface({ surfaceKey }: { surfaceKey: string }) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n Unknown surface: <code>{surfaceKey}</code>\n </div>\n );\n}\n\nconst LEGACY_AUTORUN_QUERY_PARAM = \"__autoRun\";\n\ntype SurfaceGroup = {\n parent: string; // the slug; \"\" for surfaces that declared no parent\n label: string; // display name in the sidebar\n surfaces: ClickySurface[];\n};\n\n// groupSurfacesByParent buckets surfaces by their ClickySurface.parent slug,\n// preserving the order surfaces appeared in the spec. Surfaces with no parent\n// fall into a trailing \"\" bucket labelled \"Other\".\nfunction groupSurfacesByParent(surfaces: ClickySurface[]): SurfaceGroup[] {\n const order: string[] = [];\n const buckets = new Map<string, ClickySurface[]>();\n for (const surface of surfaces) {\n const parent = surface.parent ?? \"\";\n if (!buckets.has(parent)) {\n buckets.set(parent, []);\n order.push(parent);\n }\n buckets.get(parent)!.push(surface);\n }\n return order.map((parent) => ({\n parent,\n label: parent === \"\" ? \"Other\" : titleCase(parent),\n surfaces: buckets.get(parent)!,\n }));\n}\n\nfunction titleCase(value: string): string {\n if (!value) return value;\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction readCommandQueryParams(): { initialValues: Record<string, string>; autoRun: boolean } {\n if (typeof window === \"undefined\") {\n return { initialValues: {}, autoRun: false };\n }\n const search = new URLSearchParams(window.location.search);\n const initialValues: Record<string, string> = {};\n let autoRun = false;\n const positional: string[] = [];\n\n for (const [key, value] of search.entries()) {\n if (key === AUTORUN_QUERY_PARAM || key === LEGACY_AUTORUN_QUERY_PARAM) {\n autoRun = value === \"1\" || value === \"true\";\n continue;\n }\n if (key.startsWith(ARG_QUERY_PREFIX)) {\n const index = Number.parseInt(key.slice(ARG_QUERY_PREFIX.length), 10);\n if (Number.isFinite(index) && index >= 0) {\n positional[index] = value;\n }\n continue;\n }\n initialValues[key] = value;\n }\n\n const packedArgs = positional.filter((value) => value !== undefined).join(\" \");\n if (packedArgs !== \"\") {\n initialValues.args = packedArgs;\n }\n\n return { initialValues, autoRun };\n}\n"],"mappings":";;;;;;;;;;;;;;AAmFA,IAAM,wBAAwB;AAE9B,SAAgB,kBAAkB,EAChC,QACA,WAAW,IACX,kBAAkB,MAClB,SACA,gBACA,aACA,qBACA,gBACA,0BACA,4BACA,qBACA,yBACyB;CACzB,MAAM,UAAU,gBAAgB;CAChC,MAAM,WAAW,gBAAgB;CACjC,MAAM,EAAE,UAAU,YAAY,aAAa,UAAU;CACrD,MAAM,EAAE,SAAS,cAAc,MAAM;CACrC,MAAM,WAAW,cAAc,kBAAkB,IAAI,GAAG,CAAC,IAAI,CAAC;CAC9D,MAAM,gBAAgB,cAAc,sBAAsB,QAAQ,GAAG,CAAC,QAAQ,CAAC;CAC/E,MAAM,iBAAiB,cACf,SAAS,MAAM,YAAY,CAAC,QAAQ,KAAK,KAAK,SAAS,IAC7D,CAAC,QAAQ,CACX;CACA,MAAM,eAAe,cAAc,cAAc,UAAU,QAAQ,GAAG,CAAC,UAAU,QAAQ,CAAC;CAC1F,MAAM,QAAQ,cAAc,mBAAmB,YAAY,GAAG,CAAC,YAAY,CAAC;CAC5E,MAAM,gBAAgB,cAAc,oBAAoB,OAAO,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC;CAC3F,MAAM,iBAAiB,eACd;EACL;EACA,iBAAiB,aAAa,iBAAiB,UAAU,QAAQ;EACjE,aAAa,aAAa;GACxB,MAAM,OAAO,iBAAiB,UAAU,QAAQ;GAChD,IAAI,CAAC,MAAM;GACX,SAAS,IAAI;EACf;CACF,IACA;EAAC;EAAU;EAAQ;CAAQ,CAC7B;CACA,MAAM,EAAE,eAAe,yBAAyB,cAE5C,cAAc,SAAS,YACnB,uBAAuB,IACvB;EAAE,eAAe,CAAC;EAAG,SAAS;CAAM,GAC1C,CAAC,cAAc,MAAM,QAAQ,CAC/B;CACA,MAAM,qBAAqB,eAClB;EACL,KAAK;EACL,OAAO;EACP,aAAa;CACf,IACA,CAAC,CACH;CACA,MAAM,cAAc,cAAoC;EACtD,MAAM,WAAiC,cAAc,KAAK,UACxD,kBAAkB,MAAM,OAAO,MAAM,UAAU;GAC7C,WAAW,YAAY,qBAAqB,eAAe,QAAQ,GAAG;GACtE,UAAU,YAAY,aAAa,UAAU,IAAI,QAAQ,KAAK;EAChE,CAAC,CACH;EACA,IAAI,iBACF,SAAS,KAAK,EACZ,OAAO,CACL;GACE,KAAK,mBAAmB;GACxB,OAAO,mBAAmB;GAC1B,QAAQ,cAAc,SAAS,cAAc,cAAc,SAAS;GACpE,IAAI,aAAa,UAAU,WAAW;EACxC,CACF,EACF,CAAC;EAEH,OAAO;CACT,GAAG;EAAC;EAAe;EAAU;EAAe;EAAiB;CAAkB,CAAC;CAEhF,MAAM,CAAC,cAAc,mBAAmB,SAA6B,IAAI;CACzE,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC;CAC5C,MAAM,WAAW,kBAAkB;EACjC,cAAc,UAAU,QAAQ,CAAC;EACjC,aAAa,cAAc,UAAU,QAAQ,CAAC;CAChD,GAAG,CAAC,CAAC;CACL,MAAM,UAAU,cAAc,SAAS,YAAY,cAAc,UAAU,KAAA;CAC3E,MAAM,eAAe,UAAU,sBAAsB,QAAQ,GAAG,IAAI,KAAA;CACpE,MAAM,iBAAiB,UAAU,wBAAwB,QAAQ,GAAG,IAAI,KAAA;CAGxE,MAAM,UAAU,eACP;EACL,WAAW,UAAU,eAAe;EACpC,OAAO,SAAS,SAAS;EACzB;EACA,OAAO;CACT,IACA;EAAC;EAAU;EAAc;EAAS;CAAY,CAChD;CAEA,gBAAgB;EACd,IAAI,kBAAkB,QAAQ,iBAAiB,KAAK;EACpD,SAAS,aAAa,UAAU,IAAI,eAAe,KAAK,GAAG,EAAE,SAAS,KAAK,CAAC;CAC9E,GAAG;EAAC;EAAU;EAAgB;EAAc;CAAQ,CAAC;CAErD,MAAM,UACJ,oBAAC,UAAD;EACE,OACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,MAAM,KAAK,SAAS;GAClB,CAAA,GACL,oBAAC,OAAD;IAAK,WAAU;IAA8C,UAAA;GAExD,CAAA,CACF;;EAEM;EACb,eAAe,oBAAC,eAAD,CAAgB,CAAA;EAC/B,GAAK,UAAU,EAAE,QAAQ,IAAI,CAAC;EAS9B,GAAK,cAAc,SAAS,aAAa,cAAc,UACnD,EACE,KACE,qBAAAA,YAAA,EAAA,UAAA,CACG,cAAc,KACb,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,oBAAC,MAAD;KAAI,WAAU;KACX,UAAA,cAAc,QAAQ;IACrB,CAAA;IACH,eACC,oBAAC,OAAD;KAAK,WAAU;KAA0C,UAAA;IAAkB,CAAA,IACzE;IACJ,oBAAC,KAAD;KAAG,WAAU;KACV,UAAA,sBAAsB,cAAc,OAAO,CAAC,CAAC;IAC7C,CAAA;GACA;EAEP,CAAA,GAAA,oBAAC,OAAD;GAAK,KAAK;GAAiB,aAAU;GAAe,WAAU;EAAY,CAAA,CAC1E,EAAA,CAAA,EAEN,IACA,CAAC;EACL,qBAAqB;EAGrB,cAAa;EACb,kBAAiB;EAEhB,UAAA,cAAc,SAAS,YACpB,cAAc,UACZ,oBAAC,kBAAD;GAME,YAAY,sBAAsB,cAAc,OAAO;GACvD,UAAU,CAAC,cAAc,QAAQ,MAAM;GAC/B;GACI;GACZ,YAAY,cAAc,QAAQ;GAClB;GAChB,GAAK,UAAU,EAAE,QAAQ,IAAI,CAAC;GAChC,GAAK,WAAW,EAAE,SAAS,IAAI,CAAC;GAChC,GAAK,cAAc,EAAE,YAAY,IAAI,CAAC;GACtC,GAAK,sBAAsB,cAAc,QAAQ,OAC7C,EAAE,cAAc,oBAAoB,cAAc,QAAQ,KAAK,IAC/D,CAAC;GACL,GAAK,iBAAiB,EAAE,eAAe,IAAI,CAAC;GAC5C,GAAK,iBAAiB,EAAE,eAAe,IAAI,CAAC;EAC3C,GAfM,cAAc,QAAQ,GAe5B,IAED,oBAAC,gBAAD,EAAgB,YAAY,cAAc,WAAa,CAAA,IAEvD,cAAc,SAAS,WACzB,cAAc,UACZ,oBAAC,qBAAD;GACE,YAAY,sBAAsB,cAAc,OAAO;GACvD,UAAU,CAAC,cAAc,QAAQ,MAAM;GAC/B;GACI;GACZ,YAAY,cAAc,QAAQ;GAClC,UAAU,aAAa,UAAU,IAAI,cAAc,QAAQ,KAAK;GAChE,WAAW,WAAW,cAAc,QAAQ;GAC5B;GAChB,GAAK,UAAU,EAAE,QAAQ,IAAI,CAAC;GAChC,GAAK,WAAW,EAAE,SAAS,IAAI,CAAC;GAChC,GAAK,cAAc,EAAE,YAAY,IAAI,CAAC;GACtC,GAAK,sBAAsB,cAAc,QAAQ,OAC7C,EAAE,cAAc,oBAAoB,cAAc,QAAQ,KAAK,IAC/D,CAAC;GACL,GAAK,2BAA2B,EAAE,yBAAyB,IAAI,CAAC;GAChE,GAAK,6BAA6B,EAAE,2BAA2B,IAAI,CAAC;GACpE,GAAK,cAAc,KAAK,EAAE,IAAI,cAAc,GAAG,IAAI,CAAC;EACnD,CAAA,IAED,oBAAC,gBAAD,EAAgB,YAAY,cAAc,WAAa,CAAA,IAEvD,cAAc,SAAS,YACzB,oBAAC,sBAAD;GACU;GACR,UAAU,aAAa,UAAU,WAAW;GAC5C,WAAU;GACE;GACI;GAChB,eAAe;GACf,SAAS;GACT,GAAK,cAAc,cAAc,EAAE,aAAa,cAAc,YAAY,IAAI,CAAC;EAChF,CAAA,IACC,cAAc,SAAS,aACzB,oBAAC,kBAAD;GACE,YAAY;GACZ,UAAU,CAAC;GACX,eAAA;GACQ;GACI;GACI;EACjB,CAAA,IACC,kBAAkB,OACpB,oBAAC,OAAD;GAAK,WAAU;GAAgC,UAAA;EAAsB,CAAA,IAErE,oBAAC,OAAD;GAAK,WAAU;GAAgC,UAAA;EAAiB,CAAA;CAE5D,CAAA;CAGZ,OAAO,oBAAC,mBAAmB,UAApB;EAA6B,OAAO;EAAU,UAAA;CAAqC,CAAA;AAC5F;AAcA,SAAS,mBAAmB,UAAiC;CAC3D,MAAM,aAAa,kBAAkB,QAAQ,KAAK;CAClD,IAAI,eAAe,KACjB,OAAO,EAAE,MAAM,OAAO;CAGxB,MAAM,WAAW,WAAW,QAAQ,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG;CACzD,IAAI,SAAS,OAAO,YAClB,OAAO,EAAE,MAAM,WAAW;CAE5B,IAAI,SAAS,OAAO,YAClB,OAAO,SAAS,KAAK;EAAE,MAAM;EAAW,aAAa,SAAS;CAAG,IAAI,EAAE,MAAM,UAAU;CAEzF,MAAM,aAAa,SAAS,MAAM;CAClC,IAAI,SAAS,IACX,OAAO;EACL,MAAM;EACN;EACA,IAAI,mBAAmB,SAAS,EAAE;CACpC;CAEF,OAAO;EAAE,MAAM;EAAW;CAAW;AACvC;AAEA,SAAS,oBAAoB,OAAsB,UAA0C;CAC3F,IAAI,MAAM,SAAS,aAAa,MAAM,SAAS,UAC7C,OAAO;CAET,MAAM,UAAU,SAAS,MAAM,SAAS,KAAK,QAAQ,MAAM,UAAU;CACrE,OAAO,UAAU;EAAE,GAAG;EAAO;CAAQ,IAAI;AAC3C;AAEA,SAAS,qBAAqB,OAAsB,YAAoB;CACtE,OACG,MAAM,SAAS,aAAa,MAAM,eAAe,cACjD,MAAM,SAAS,YAAY,MAAM,eAAe;AAErD;AAEA,SAAS,cAAc,UAAkB,UAAkB;CACzD,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAAC,QAAQ,SAAS,KACpB,OAAO,kBAAkB,QAAQ,KAAK;CAExC,IAAI,aAAa,MACf,OAAO;CAET,IAAI,SAAS,WAAW,GAAG,KAAK,EAAE,GAChC,OAAO,kBAAkB,SAAS,MAAM,KAAK,MAAM,CAAC,KAAK;CAE3D,OAAO,kBAAkB,QAAQ,KAAK;AACxC;AAEA,SAAS,eAAe,EAAE,cAAsC;CAC9D,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CAAmD,qBAChC,oBAAC,QAAD,EAAA,UAAO,WAAiB,CAAA,CACtC;;AAET;AAEA,IAAM,6BAA6B;AAWnC,SAAS,sBAAsB,UAA2C;CACxE,MAAM,QAAkB,CAAC;CACzB,MAAM,0BAAU,IAAI,IAA6B;CACjD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,SAAS,QAAQ,UAAU;EACjC,IAAI,CAAC,QAAQ,IAAI,MAAM,GAAG;GACxB,QAAQ,IAAI,QAAQ,CAAC,CAAC;GACtB,MAAM,KAAK,MAAM;EACnB;EACA,QAAQ,IAAI,MAAM,CAAC,CAAE,KAAK,OAAO;CACnC;CACA,OAAO,MAAM,KAAK,YAAY;EAC5B;EACA,OAAO,WAAW,KAAK,UAAU,UAAU,MAAM;EACjD,UAAU,QAAQ,IAAI,MAAM;CAC9B,EAAE;AACJ;AAEA,SAAS,UAAU,OAAuB;CACxC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AAEA,SAAS,yBAAsF;CAC7F,IAAI,OAAO,WAAW,aACpB,OAAO;EAAE,eAAe,CAAC;EAAG,SAAS;CAAM;CAE7C,MAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;CACzD,MAAM,gBAAwC,CAAC;CAC/C,IAAI,UAAU;CACd,MAAM,aAAuB,CAAC;CAE9B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG;EAC3C,IAAI,QAAA,aAA+B,QAAQ,4BAA4B;GACrE,UAAU,UAAU,OAAO,UAAU;GACrC;EACF;EACA,IAAI,IAAI,WAAA,OAA2B,GAAG;GACpC,MAAM,QAAQ,OAAO,SAAS,IAAI,MAAM,iBAAiB,MAAM,GAAG,EAAE;GACpE,IAAI,OAAO,SAAS,KAAK,KAAK,SAAS,GACrC,WAAW,SAAS;GAEtB;EACF;EACA,cAAc,OAAO;CACvB;CAEA,MAAM,aAAa,WAAW,QAAQ,UAAU,UAAU,KAAA,CAAS,CAAC,CAAC,KAAK,GAAG;CAC7E,IAAI,eAAe,IACjB,cAAc,OAAO;CAGvB,OAAO;EAAE;EAAe;CAAQ;AAClC"}