@spacesync/client 0.1.0

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 (100) hide show
  1. package/README.md +39 -0
  2. package/dist/analog-clock-picker.d.ts +10 -0
  3. package/dist/analog-clock-picker.js +408 -0
  4. package/dist/analog-clock-picker.js.map +1 -0
  5. package/dist/api.d.ts +222 -0
  6. package/dist/api.js +167 -0
  7. package/dist/api.js.map +1 -0
  8. package/dist/app-modal.d.ts +25 -0
  9. package/dist/app-modal.js +145 -0
  10. package/dist/app-modal.js.map +1 -0
  11. package/dist/app-shell-mobile-nav.d.ts +29 -0
  12. package/dist/app-shell-mobile-nav.js +122 -0
  13. package/dist/app-shell-mobile-nav.js.map +1 -0
  14. package/dist/app-web-shell.d.ts +20 -0
  15. package/dist/app-web-shell.js +45 -0
  16. package/dist/app-web-shell.js.map +1 -0
  17. package/dist/booking-schedule-calendars.d.ts +95 -0
  18. package/dist/booking-schedule-calendars.js +1343 -0
  19. package/dist/booking-schedule-calendars.js.map +1 -0
  20. package/dist/chat-stick-to-bottom.d.ts +22 -0
  21. package/dist/chat-stick-to-bottom.js +190 -0
  22. package/dist/chat-stick-to-bottom.js.map +1 -0
  23. package/dist/detail-page-header.d.ts +16 -0
  24. package/dist/detail-page-header.js +220 -0
  25. package/dist/detail-page-header.js.map +1 -0
  26. package/dist/dialog-CIdvylPK.d.ts +16 -0
  27. package/dist/errors-D1niseaa.d.ts +34 -0
  28. package/dist/file-picker-field.d.ts +23 -0
  29. package/dist/file-picker-field.js +165 -0
  30. package/dist/file-picker-field.js.map +1 -0
  31. package/dist/file-viewer-dialog.d.ts +15 -0
  32. package/dist/file-viewer-dialog.js +387 -0
  33. package/dist/file-viewer-dialog.js.map +1 -0
  34. package/dist/floor-switcher.d.ts +14 -0
  35. package/dist/floor-switcher.js +416 -0
  36. package/dist/floor-switcher.js.map +1 -0
  37. package/dist/forms.d.ts +52 -0
  38. package/dist/forms.js +66 -0
  39. package/dist/forms.js.map +1 -0
  40. package/dist/image-picker-field.d.ts +11 -0
  41. package/dist/image-picker-field.js +135 -0
  42. package/dist/image-picker-field.js.map +1 -0
  43. package/dist/layout-canvas-view.d.ts +57 -0
  44. package/dist/layout-canvas-view.js +974 -0
  45. package/dist/layout-canvas-view.js.map +1 -0
  46. package/dist/lib/dateDisplay.d.ts +24 -0
  47. package/dist/lib/dateDisplay.js +84 -0
  48. package/dist/lib/dateDisplay.js.map +1 -0
  49. package/dist/live-resource-meta.d.ts +11 -0
  50. package/dist/live-resource-meta.js +27 -0
  51. package/dist/live-resource-meta.js.map +1 -0
  52. package/dist/live-space-pulse-dot.d.ts +8 -0
  53. package/dist/live-space-pulse-dot.js +18 -0
  54. package/dist/live-space-pulse-dot.js.map +1 -0
  55. package/dist/notification-sidebar-preview.d.ts +37 -0
  56. package/dist/notification-sidebar-preview.js +121 -0
  57. package/dist/notification-sidebar-preview.js.map +1 -0
  58. package/dist/page-list-chrome.d.ts +124 -0
  59. package/dist/page-list-chrome.js +970 -0
  60. package/dist/page-list-chrome.js.map +1 -0
  61. package/dist/platform-support-message-composer.d.ts +38 -0
  62. package/dist/platform-support-message-composer.js +446 -0
  63. package/dist/platform-support-message-composer.js.map +1 -0
  64. package/dist/shell-sidebar-nav.d.ts +16 -0
  65. package/dist/shell-sidebar-nav.js +69 -0
  66. package/dist/shell-sidebar-nav.js.map +1 -0
  67. package/dist/shell-theme.d.ts +6 -0
  68. package/dist/shell-theme.js +9 -0
  69. package/dist/shell-theme.js.map +1 -0
  70. package/dist/shell-top-bar-chrome.d.ts +39 -0
  71. package/dist/shell-top-bar-chrome.js +105 -0
  72. package/dist/shell-top-bar-chrome.js.map +1 -0
  73. package/dist/shell-top-bar.d.ts +16 -0
  74. package/dist/shell-top-bar.js +71 -0
  75. package/dist/shell-top-bar.js.map +1 -0
  76. package/dist/styles/theme.css +223 -0
  77. package/dist/styles/theme.css.map +1 -0
  78. package/dist/styles/theme.d.ts +2 -0
  79. package/dist/support-presence-status.d.ts +11 -0
  80. package/dist/support-presence-status.js +92 -0
  81. package/dist/support-presence-status.js.map +1 -0
  82. package/dist/theme-appearance-control.d.ts +5 -0
  83. package/dist/theme-appearance-control.js +114 -0
  84. package/dist/theme-appearance-control.js.map +1 -0
  85. package/dist/theme-boot-script.d.ts +4 -0
  86. package/dist/theme-boot-script.js +9 -0
  87. package/dist/theme-boot-script.js.map +1 -0
  88. package/dist/theme-provider.d.ts +9 -0
  89. package/dist/theme-provider.js +25 -0
  90. package/dist/theme-provider.js.map +1 -0
  91. package/dist/ui.d.ts +765 -0
  92. package/dist/ui.js +5637 -0
  93. package/dist/ui.js.map +1 -0
  94. package/dist/use-client-pagination.d.ts +22 -0
  95. package/dist/use-client-pagination.js +51 -0
  96. package/dist/use-client-pagination.js.map +1 -0
  97. package/dist/use-client-sort.d.ts +20 -0
  98. package/dist/use-client-sort.js +41 -0
  99. package/dist/use-client-sort.js.map +1 -0
  100. package/package.json +208 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/support-attachments.ts","../src/ui/utils.ts","../src/ui/button.tsx","../src/files/file-picker-field.tsx","../src/ui/tooltip.tsx","../src/ui/textarea.tsx","../src/support/platform-support-message-composer.tsx"],"names":["Button","jsx","jsxs","React2","Textarea","Paperclip","ImageIcon"],"mappings":"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","file":"platform-support-message-composer.js","sourcesContent":["/**\n * Support attachment helpers for the shared message composer.\n * Copied from `@spacesync/client/domain` so the client has no package dependency.\n */\n\nexport const MAX_ATTACHMENTS_PER_MESSAGE = 5;\n\nexport const MAX_SUPPORT_ATTACHMENT_BYTES = 5 * 1024 * 1024;\n\nconst ALLOWED_MIME_PREFIXES = [\"image/\", \"application/pdf\", \"text/\"];\nconst ALLOWED_MIME_EXACT = new Set([\n \"application/zip\",\n \"application/json\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n]);\n\nexport type SupportAttachmentValidationError =\n | { kind: \"too_big\"; max: number }\n | { kind: \"unsupported_type\" };\n\n/** Same shape as platform support attachments from the API. */\nexport interface SupportAttachment {\n id: string;\n name: string;\n mimeType: string;\n sizeBytes: number;\n storageKey: string;\n uploadedAt: string;\n /** Short-lived signed URL, populated by the API on read — not stored in Mongo. */\n readUrl?: string;\n}\n\nexport function validateSupportAttachmentFile(file: {\n size: number;\n type: string;\n}): SupportAttachmentValidationError | null {\n if (file.size > MAX_SUPPORT_ATTACHMENT_BYTES) {\n return { kind: \"too_big\", max: MAX_SUPPORT_ATTACHMENT_BYTES };\n }\n const mime = file.type || \"application/octet-stream\";\n const allowed =\n ALLOWED_MIME_EXACT.has(mime) || ALLOWED_MIME_PREFIXES.some((prefix) => mime.startsWith(prefix));\n if (!allowed) {\n return { kind: \"unsupported_type\" };\n }\n return null;\n}\n\nexport function describeSupportAttachmentError(err: SupportAttachmentValidationError): string {\n if (err.kind === \"too_big\") {\n const mb = (err.max / (1024 * 1024)).toFixed(1);\n return `File too large (max ${mb} MB).`;\n }\n return \"Unsupported file type.\";\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"./utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 active:scale-[0.98]\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80 active:scale-[0.98] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 dark:active:bg-destructive/70\",\n outline:\n \"border bg-background text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 active:scale-[0.98] dark:bg-input/30 dark:border-input dark:hover:bg-input/50 dark:active:bg-input/60\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 active:scale-[0.98]\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground active:bg-accent/90 active:scale-[0.98] dark:hover:bg-accent/50 dark:active:bg-accent/40\",\n link: \"text-primary underline-offset-4 hover:underline active:text-primary/80\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9 rounded-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nconst Button = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }\n>(function Button({ className, variant, size, asChild = false, ...props }, ref) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n />\n );\n});\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import { useRef, type ReactNode } from \"react\";\nimport { Paperclip, Upload } from \"lucide-react\";\nimport { Button } from \"../ui/button\";\nimport { cn } from \"../ui/utils\";\n\nexport type FilePickerFieldVariant = \"card\" | \"button\" | \"inline\";\n\nexport interface FilePickerFieldProps {\n label?: string;\n actionLabel?: string;\n description?: string;\n selectedLabel?: string;\n accept?: string;\n multiple?: boolean;\n disabled?: boolean;\n variant?: FilePickerFieldVariant;\n icon?: ReactNode;\n className?: string;\n triggerClassName?: string;\n inputClassName?: string;\n title?: string;\n onFilesSelected: (files: FileList | null) => void;\n}\n\nexport function FilePickerField({\n label,\n actionLabel,\n description,\n selectedLabel,\n accept,\n multiple = false,\n disabled = false,\n variant = \"card\",\n icon,\n className,\n triggerClassName,\n inputClassName,\n title,\n onFilesSelected,\n}: FilePickerFieldProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const triggerLabel = selectedLabel ?? actionLabel ?? (multiple ? \"Choose files\" : \"Choose file\");\n const fallbackIcon = variant === \"card\" ? (\n <Upload size={16} className=\"text-muted-foreground\" />\n ) : (\n <Paperclip size={13} />\n );\n\n const handleFiles = (files: FileList | null) => {\n if (disabled) return;\n onFilesSelected(files);\n if (inputRef.current) inputRef.current.value = \"\";\n };\n\n const input = (\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n className={cn(\"hidden\", inputClassName)}\n onChange={(event) => handleFiles(event.target.files)}\n />\n );\n\n if (variant === \"button\") {\n return (\n <div className={className}>\n {input}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n title={title}\n className={triggerClassName}\n onClick={() => inputRef.current?.click()}\n >\n {icon ?? fallbackIcon}\n {triggerLabel}\n </Button>\n </div>\n );\n }\n\n if (variant === \"inline\") {\n return (\n <span className={className}>\n {input}\n <button\n type=\"button\"\n disabled={disabled}\n title={title}\n className={cn(\n \"inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-blue-600 disabled:pointer-events-none disabled:text-muted-foreground/60 dark:text-blue-400\",\n triggerClassName,\n )}\n onClick={() => inputRef.current?.click()}\n >\n {icon ?? fallbackIcon}\n {triggerLabel}\n </button>\n </span>\n );\n }\n\n return (\n <div className={cn(\"flex flex-col gap-1.5\", className)}>\n {label ? (\n <span className=\"text-[10px] font-medium uppercase tracking-widest text-muted-foreground\">\n {label}\n </span>\n ) : null}\n {input}\n <button\n type=\"button\"\n disabled={disabled}\n title={title}\n className={cn(\n \"flex cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed border-border bg-muted/80 px-4 py-6 text-center transition-colors hover:border-border hover:bg-accent disabled:pointer-events-none disabled:opacity-60\",\n triggerClassName,\n )}\n onClick={() => inputRef.current?.click()}\n onDragOver={(event) => {\n if (disabled) return;\n event.preventDefault();\n }}\n onDrop={(event) => {\n if (disabled) return;\n event.preventDefault();\n handleFiles(event.dataTransfer.files);\n }}\n >\n <span className=\"mb-1 flex size-8 items-center justify-center rounded-lg bg-muted\">\n {icon ?? fallbackIcon}\n </span>\n <span className=\"text-xs font-medium text-muted-foreground\">{triggerLabel}</span>\n {description ? (\n <span className=\"mt-0.5 text-[10px] text-muted-foreground\">{description}</span>\n ) : null}\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"./utils\";\n\nfunction TooltipProvider({\n delayDuration = 0,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return (\n <TooltipPrimitive.Provider\n data-slot=\"tooltip-provider\"\n delayDuration={delayDuration}\n {...props}\n />\n );\n}\n\nfunction Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n </TooltipProvider>\n );\n}\n\nfunction TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\n}\n\nfunction TooltipContent({\n className,\n sideOffset = 0,\n children,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n \"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance\",\n className,\n )}\n {...props}\n >\n {children}\n <TooltipPrimitive.Arrow className=\"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]\" />\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n );\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nconst Textarea = React.forwardRef<\n HTMLTextAreaElement,\n React.ComponentProps<\"textarea\">\n>(function Textarea({ className, ...props }, ref) {\n return (\n <textarea\n ref={ref}\n data-slot=\"textarea\"\n className={cn(\n \"resize-none border-border bg-card text-foreground placeholder:text-muted-foreground focus-visible:border-blue-400 focus-visible:ring-blue-500/25 aria-invalid:border-red-400 aria-invalid:ring-red-500/20 flex field-sizing-content min-h-16 w-full rounded-md border px-3 py-2 text-base transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className,\n )}\n {...props}\n />\n );\n});\n\nexport { Textarea };\n","import { useCallback, useState } from \"react\";\nimport { FileText, Image as ImageIcon, Paperclip, Plus, Send, X } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n MAX_ATTACHMENTS_PER_MESSAGE,\n describeSupportAttachmentError,\n validateSupportAttachmentFile,\n type SupportAttachment,\n} from \"../lib/support-attachments\";\nimport { FilePickerField } from \"../files/file-picker-field\";\nimport { Button, Textarea, Tooltip, TooltipContent, TooltipTrigger, cn } from \"../ui\";\n\nexport type { SupportAttachment };\n\nexport function PlatformSupportAttachmentChip({\n attachment,\n onRemove,\n}: {\n attachment: SupportAttachment;\n onRemove?: () => void;\n}) {\n return (\n <li className=\"inline-flex items-center gap-1.5 rounded-lg border border-border bg-card px-2.5 py-1 text-xs text-muted-foreground\">\n <Paperclip size={11} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"max-w-[140px] truncate\">{attachment.name}</span>\n {onRemove ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={onRemove}\n className=\"size-5 text-muted-foreground hover:text-red-500\"\n aria-label={`Remove ${attachment.name}`}\n >\n <X size={11} />\n </Button>\n ) : null}\n </li>\n );\n}\n\nexport function PlatformSupportAttachmentPreview({\n attachment,\n className,\n}: {\n attachment: SupportAttachment;\n className?: string;\n}) {\n const href = attachment.readUrl;\n const isImage = attachment.mimeType.startsWith(\"image/\");\n const inner = (\n <>\n {isImage ? (\n <ImageIcon size={12} className=\"shrink-0 text-muted-foreground\" />\n ) : (\n <FileText size={12} className=\"shrink-0 text-muted-foreground\" />\n )}\n <span className=\"min-w-0 flex-1 truncate font-medium\">{attachment.name}</span>\n <span className=\"shrink-0 text-[10px] text-muted-foreground\">\n {Math.round(attachment.sizeBytes / 1024)} KB\n </span>\n </>\n );\n if (!href) {\n return (\n <span\n className={cn(\n \"flex max-w-[220px] items-center gap-2 rounded-lg border border-border bg-card px-2.5 py-1.5 text-xs text-muted-foreground\",\n className,\n )}\n title={attachment.name}\n >\n {inner}\n </span>\n );\n }\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noreferrer\"\n download={attachment.name}\n className={cn(\n \"flex max-w-[220px] items-center gap-2 rounded-lg border border-border bg-card px-2.5 py-1.5 text-xs text-muted-foreground hover:bg-accent\",\n className,\n )}\n title={attachment.name}\n >\n {inner}\n </a>\n );\n}\n\nexport type PlatformSupportMessageComposerProps = {\n value: string;\n onChange: (value: string) => void;\n attachments: SupportAttachment[];\n onAttachmentsChange: (attachments: SupportAttachment[]) => void;\n onSend: () => void;\n onUploadFile: (file: File) => Promise<SupportAttachment>;\n onTyping?: () => void;\n placeholder?: string;\n disabled?: boolean;\n compact?: boolean;\n className?: string;\n};\n\nexport function PlatformSupportMessageComposer({\n value,\n onChange,\n attachments,\n onAttachmentsChange,\n onSend,\n onUploadFile,\n onTyping,\n placeholder = \"Write a message…\",\n disabled = false,\n compact = false,\n className,\n}: PlatformSupportMessageComposerProps) {\n const [busy, setBusy] = useState(false);\n\n const canSend = !disabled && (value.trim().length > 0 || attachments.length > 0);\n\n const handleFiles = useCallback(\n async (files: FileList | null) => {\n if (!files || files.length === 0) return;\n const remaining = MAX_ATTACHMENTS_PER_MESSAGE - attachments.length;\n if (remaining <= 0) {\n toast.error(`Maximum ${MAX_ATTACHMENTS_PER_MESSAGE} attachments per message`);\n return;\n }\n setBusy(true);\n try {\n const next = [...attachments];\n for (const file of Array.from(files).slice(0, remaining)) {\n const err = validateSupportAttachmentFile(file);\n if (err) {\n toast.error(describeSupportAttachmentError(err));\n continue;\n }\n next.push(await onUploadFile(file));\n }\n onAttachmentsChange(next);\n } catch {\n toast.error(\"Could not upload file\");\n } finally {\n setBusy(false);\n }\n },\n [attachments, onAttachmentsChange, onUploadFile],\n );\n\n const removeAttachment = (id: string) => {\n onAttachmentsChange(attachments.filter((a) => a.id !== id));\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n if (canSend) onSend();\n }\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {attachments.length > 0 ? (\n <ul className=\"flex flex-wrap gap-1.5\">\n {attachments.map((attachment) => (\n <PlatformSupportAttachmentChip\n key={attachment.id}\n attachment={attachment}\n onRemove={() => removeAttachment(attachment.id)}\n />\n ))}\n </ul>\n ) : null}\n\n <div\n className={cn(\n \"rounded-xl border border-border bg-card px-2.5 py-2 shadow-sm\",\n disabled && \"opacity-60\",\n )}\n >\n <div className=\"flex items-end gap-2\">\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex shrink-0\">\n <FilePickerField\n variant=\"inline\"\n multiple\n disabled={disabled || busy || attachments.length >= MAX_ATTACHMENTS_PER_MESSAGE}\n actionLabel=\"\"\n icon={<Plus size={compact ? 16 : 18} strokeWidth={1.75} />}\n triggerClassName={cn(\n \"inline-flex items-center justify-center rounded-lg border border-transparent text-muted-foreground transition-colors hover:border-border hover:bg-muted hover:text-foreground\",\n compact ? \"size-8\" : \"size-9\",\n )}\n title=\"Attach files\"\n onFilesSelected={(files) => void handleFiles(files)}\n />\n </span>\n </TooltipTrigger>\n <TooltipContent side=\"top\">Attach</TooltipContent>\n </Tooltip>\n <Textarea\n value={value}\n onChange={(e) => {\n onChange(e.target.value);\n onTyping?.();\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled || busy}\n rows={1}\n className={cn(\n \"max-h-28 min-h-[36px] flex-1 resize-none border-0 bg-transparent px-0.5 py-1.5 text-sm shadow-none focus-visible:ring-0 focus-visible:ring-offset-0\",\n compact && \"min-h-[32px] text-xs\",\n )}\n />\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n size=\"icon\"\n onClick={onSend}\n disabled={!canSend || busy}\n aria-label=\"Send message\"\n className={cn(\n \"shrink-0 rounded-lg bg-blue-500 text-white hover:bg-blue-600 disabled:bg-muted disabled:text-muted-foreground\",\n compact ? \"size-8\" : \"size-9\",\n )}\n >\n <Send size={compact ? 14 : 16} strokeWidth={2} />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"top\">Send</TooltipContent>\n </Tooltip>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,16 @@
1
+ /** Nav link / button styles for {@link AppWebShell}'s sidebar column. */
2
+ declare function shellSidebarNavItemClass(isActive: boolean, collapsed: boolean): string;
3
+ declare function shellSidebarNavIconClass(isActive: boolean): "text-sidebar-foreground/55" | "text-blue-400" | "text-blue-600";
4
+ declare function shellSidebarCollapseButtonClass(collapsed: boolean): string;
5
+ /** Sub-item row inside an expanded nav group (e.g. Bookings → Today's Booking). */
6
+ declare function shellSidebarNavSubItemClass(isActive: boolean): string;
7
+ /** Parent row for a nav group with expandable children. */
8
+ declare function shellSidebarNavGroupHeaderClass(isSectionActive: boolean, collapsed: boolean, isOpen: boolean): string;
9
+ /** Flyout sub-item (collapsed sidebar hover panel). */
10
+ declare function shellSidebarFlyoutSubItemClass(isActive: boolean): string;
11
+ /** Animated wrapper for expandable submenu lists. */
12
+ declare function shellSidebarNavSubItemsContainerClass(isOpen: boolean): string;
13
+ /** Numeric count badge on submenu rows or collapsed group icons. */
14
+ declare function shellSidebarNavCountBadgeClass(collapsedIcon?: boolean): string;
15
+
16
+ export { shellSidebarCollapseButtonClass, shellSidebarFlyoutSubItemClass, shellSidebarNavCountBadgeClass, shellSidebarNavGroupHeaderClass, shellSidebarNavIconClass, shellSidebarNavItemClass, shellSidebarNavSubItemClass, shellSidebarNavSubItemsContainerClass };
@@ -0,0 +1,69 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // src/theme/shell-theme.ts
5
+ function getShellTheme() {
6
+ return "light";
7
+ }
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ // src/shell/shell-sidebar-nav.ts
13
+ var shellSidebarButtonReset = "font-normal shadow-none hover:bg-sidebar-accent hover:text-sidebar-foreground [&_svg]:size-[18px]";
14
+ function shellSidebarNavItemClass(isActive, collapsed) {
15
+ return cn(
16
+ "relative flex w-full items-center rounded-lg border text-left text-sm transition-colors",
17
+ shellSidebarButtonReset,
18
+ collapsed ? "h-9 justify-center px-0 has-[>svg]:px-0" : "justify-start gap-3 px-2.5 py-2 has-[>svg]:px-2.5",
19
+ isActive ? "border-blue-200/60 bg-blue-50 text-blue-700" : "border-transparent text-sidebar-foreground/65 hover:bg-sidebar-accent hover:text-sidebar-foreground"
20
+ );
21
+ }
22
+ function shellSidebarNavIconClass(isActive) {
23
+ const isDarkShell = getShellTheme() === "dark";
24
+ if (!isActive) return "text-sidebar-foreground/55";
25
+ return isDarkShell ? "text-blue-400" : "text-blue-600";
26
+ }
27
+ function shellSidebarCollapseButtonClass(collapsed) {
28
+ return cn(
29
+ shellSidebarNavItemClass(false, collapsed),
30
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/30"
31
+ );
32
+ }
33
+ function shellSidebarNavSubItemClass(isActive) {
34
+ return cn(
35
+ "relative flex w-full items-center rounded-md border text-left text-sm transition-colors",
36
+ shellSidebarButtonReset,
37
+ "justify-start gap-2 py-1.5 pl-9 pr-2.5",
38
+ isActive ? "border-blue-200/60 bg-blue-50 text-blue-700" : "border-transparent text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-foreground"
39
+ );
40
+ }
41
+ function shellSidebarNavGroupHeaderClass(isSectionActive, collapsed, isOpen) {
42
+ return cn(
43
+ shellSidebarNavItemClass(isSectionActive && !isOpen, collapsed),
44
+ !collapsed && isOpen && isSectionActive && "border-transparent bg-transparent text-sidebar-foreground/65"
45
+ );
46
+ }
47
+ function shellSidebarFlyoutSubItemClass(isActive) {
48
+ return cn(
49
+ "flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-sm transition-colors",
50
+ isActive ? "bg-blue-500/20 font-medium text-popover-foreground" : "text-popover-foreground/85 hover:bg-white/10 hover:text-popover-foreground"
51
+ );
52
+ }
53
+ function shellSidebarNavSubItemsContainerClass(isOpen) {
54
+ return cn(
55
+ "grid transition-[grid-template-rows] duration-200 ease-out",
56
+ isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
57
+ );
58
+ }
59
+ function shellSidebarNavCountBadgeClass(collapsedIcon = false) {
60
+ return cn(
61
+ "pointer-events-none flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1 text-[10px] font-semibold tabular-nums",
62
+ collapsedIcon ? "absolute right-0.5 top-0.5" : "ml-auto",
63
+ "bg-amber-100 text-amber-700"
64
+ );
65
+ }
66
+
67
+ export { shellSidebarCollapseButtonClass, shellSidebarFlyoutSubItemClass, shellSidebarNavCountBadgeClass, shellSidebarNavGroupHeaderClass, shellSidebarNavIconClass, shellSidebarNavItemClass, shellSidebarNavSubItemClass, shellSidebarNavSubItemsContainerClass };
68
+ //# sourceMappingURL=shell-sidebar-nav.js.map
69
+ //# sourceMappingURL=shell-sidebar-nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme/shell-theme.ts","../src/ui/utils.ts","../src/shell/shell-sidebar-nav.ts"],"names":[],"mappings":";;;;AAKO,SAAS,aAAA,GAA4B;AAC1C,EAAA,OAAuC,OAAA;AACzC;ACJO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;;;ACDA,IAAM,uBAAA,GACJ,mGAAA;AAGK,SAAS,wBAAA,CAAyB,UAAmB,SAAA,EAAoB;AAG9E,EAAA,OAAO,EAAA;AAAA,IACL,yFAAA;AAAA,IACA,uBAAA;AAAA,IACA,YACI,yCAAA,GACA,mDAAA;AAAA,IACJ,QAAA,GAGM,6CAAA,GACF;AAAA,GACN;AACF;AAEO,SAAS,yBAAyB,QAAA,EAAmB;AAC1D,EAAA,MAAM,WAAA,GAAc,eAAc,KAAM,MAAA;AACxC,EAAA,IAAI,CAAC,UAAU,OAAO,4BAAA;AACtB,EAAA,OAAO,cAAc,eAAA,GAAkB,eAAA;AACzC;AAEO,SAAS,gCAAgC,SAAA,EAAoB;AAClE,EAAA,OAAO,EAAA;AAAA,IACL,wBAAA,CAAyB,OAAO,SAAS,CAAA;AAAA,IACzC;AAAA,GACF;AACF;AAGO,SAAS,4BAA4B,QAAA,EAAmB;AAG7D,EAAA,OAAO,EAAA;AAAA,IACL,yFAAA;AAAA,IACA,uBAAA;AAAA,IACA,wCAAA;AAAA,IACA,QAAA,GAGM,6CAAA,GACF;AAAA,GACN;AACF;AAGO,SAAS,+BAAA,CACd,eAAA,EACA,SAAA,EACA,MAAA,EACA;AACA,EAAA,OAAO,EAAA;AAAA,IACL,wBAAA,CAAyB,eAAA,IAAmB,CAAC,MAAA,EAAQ,SAAS,CAAA;AAAA,IAC9D,CAAC,SAAA,IAAa,MAAA,IAAU,eAAA,IAAmB;AAAA,GAC7C;AACF;AAGO,SAAS,+BAA+B,QAAA,EAAmB;AAChE,EAAA,OAAO,EAAA;AAAA,IACL,iGAAA;AAAA,IACA,WACI,oDAAA,GACA;AAAA,GACN;AACF;AAGO,SAAS,sCAAsC,MAAA,EAAiB;AACrE,EAAA,OAAO,EAAA;AAAA,IACL,4DAAA;AAAA,IACA,SAAS,iBAAA,GAAoB;AAAA,GAC/B;AACF;AAGO,SAAS,8BAAA,CAA+B,gBAAgB,KAAA,EAAO;AAGpE,EAAA,OAAO,EAAA;AAAA,IACL,oIAAA;AAAA,IACA,gBAAgB,4BAAA,GAA+B,SAAA;AAAA,IACE;AAAA,GACnD;AACF","file":"shell-sidebar-nav.js","sourcesContent":["/** Flip to compare light vs dark outer shell with the team. */\nexport const SHELL_USE_DARK_THEME = false;\n\nexport type ShellTheme = \"light\" | \"dark\";\n\nexport function getShellTheme(): ShellTheme {\n return SHELL_USE_DARK_THEME ? \"dark\" : \"light\";\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { getShellTheme } from \"../theme/shell-theme\";\nimport { cn } from \"../ui/utils\";\n\n/** Neutralize default {@link Button} chrome when it doubles as a sidebar nav row. */\nconst shellSidebarButtonReset =\n \"font-normal shadow-none hover:bg-sidebar-accent hover:text-sidebar-foreground [&_svg]:size-[18px]\";\n\n/** Nav link / button styles for {@link AppWebShell}'s sidebar column. */\nexport function shellSidebarNavItemClass(isActive: boolean, collapsed: boolean) {\n const isDarkShell = getShellTheme() === \"dark\";\n\n return cn(\n \"relative flex w-full items-center rounded-lg border text-left text-sm transition-colors\",\n shellSidebarButtonReset,\n collapsed\n ? \"h-9 justify-center px-0 has-[>svg]:px-0\"\n : \"justify-start gap-3 px-2.5 py-2 has-[>svg]:px-2.5\",\n isActive\n ? isDarkShell\n ? \"border-blue-500/25 bg-blue-500/15 text-blue-300\"\n : \"border-blue-200/60 bg-blue-50 text-blue-700\"\n : \"border-transparent text-sidebar-foreground/65 hover:bg-sidebar-accent hover:text-sidebar-foreground\",\n );\n}\n\nexport function shellSidebarNavIconClass(isActive: boolean) {\n const isDarkShell = getShellTheme() === \"dark\";\n if (!isActive) return \"text-sidebar-foreground/55\";\n return isDarkShell ? \"text-blue-400\" : \"text-blue-600\";\n}\n\nexport function shellSidebarCollapseButtonClass(collapsed: boolean) {\n return cn(\n shellSidebarNavItemClass(false, collapsed),\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/30\",\n );\n}\n\n/** Sub-item row inside an expanded nav group (e.g. Bookings → Today's Booking). */\nexport function shellSidebarNavSubItemClass(isActive: boolean) {\n const isDarkShell = getShellTheme() === \"dark\";\n\n return cn(\n \"relative flex w-full items-center rounded-md border text-left text-sm transition-colors\",\n shellSidebarButtonReset,\n \"justify-start gap-2 py-1.5 pl-9 pr-2.5\",\n isActive\n ? isDarkShell\n ? \"border-blue-500/25 bg-blue-500/15 text-blue-300\"\n : \"border-blue-200/60 bg-blue-50 text-blue-700\"\n : \"border-transparent text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-foreground\",\n );\n}\n\n/** Parent row for a nav group with expandable children. */\nexport function shellSidebarNavGroupHeaderClass(\n isSectionActive: boolean,\n collapsed: boolean,\n isOpen: boolean,\n) {\n return cn(\n shellSidebarNavItemClass(isSectionActive && !isOpen, collapsed),\n !collapsed && isOpen && isSectionActive && \"border-transparent bg-transparent text-sidebar-foreground/65\",\n );\n}\n\n/** Flyout sub-item (collapsed sidebar hover panel). */\nexport function shellSidebarFlyoutSubItemClass(isActive: boolean) {\n return cn(\n \"flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-sm transition-colors\",\n isActive\n ? \"bg-blue-500/20 font-medium text-popover-foreground\"\n : \"text-popover-foreground/85 hover:bg-white/10 hover:text-popover-foreground\",\n );\n}\n\n/** Animated wrapper for expandable submenu lists. */\nexport function shellSidebarNavSubItemsContainerClass(isOpen: boolean) {\n return cn(\n \"grid transition-[grid-template-rows] duration-200 ease-out\",\n isOpen ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\",\n );\n}\n\n/** Numeric count badge on submenu rows or collapsed group icons. */\nexport function shellSidebarNavCountBadgeClass(collapsedIcon = false) {\n const isDarkShell = getShellTheme() === \"dark\";\n\n return cn(\n \"pointer-events-none flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1 text-[10px] font-semibold tabular-nums\",\n collapsedIcon ? \"absolute right-0.5 top-0.5\" : \"ml-auto\",\n isDarkShell ? \"bg-amber-500/20 text-amber-300\" : \"bg-amber-100 text-amber-700\",\n );\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /** Flip to compare light vs dark outer shell with the team. */
2
+ declare const SHELL_USE_DARK_THEME = false;
3
+ type ShellTheme = "light" | "dark";
4
+ declare function getShellTheme(): ShellTheme;
5
+
6
+ export { SHELL_USE_DARK_THEME, type ShellTheme, getShellTheme };
@@ -0,0 +1,9 @@
1
+ // src/theme/shell-theme.ts
2
+ var SHELL_USE_DARK_THEME = false;
3
+ function getShellTheme() {
4
+ return "light";
5
+ }
6
+
7
+ export { SHELL_USE_DARK_THEME, getShellTheme };
8
+ //# sourceMappingURL=shell-theme.js.map
9
+ //# sourceMappingURL=shell-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme/shell-theme.ts"],"names":[],"mappings":";AACO,IAAM,oBAAA,GAAuB;AAI7B,SAAS,aAAA,GAA4B;AAC1C,EAAA,OAAuC,OAAA;AACzC","file":"shell-theme.js","sourcesContent":["/** Flip to compare light vs dark outer shell with the team. */\nexport const SHELL_USE_DARK_THEME = false;\n\nexport type ShellTheme = \"light\" | \"dark\";\n\nexport function getShellTheme(): ShellTheme {\n return SHELL_USE_DARK_THEME ? \"dark\" : \"light\";\n}\n"]}
@@ -0,0 +1,39 @@
1
+ declare const shellTopBarMutedIconClass = "text-sidebar-foreground/60";
2
+ declare const shellTopBarTitleTextClass = "text-sidebar-foreground";
3
+ /** Apply to native shell actions (links, ticket buttons) so they share the same hover treatment as {@link ShellTopBarSecondaryActionButton}. */
4
+ declare const shellTopBarNeutralActionDataAttr: {
5
+ readonly "data-shell-top-bar-action": "";
6
+ };
7
+ /** Compact back control in the shell top bar (detail pages). */
8
+ declare const shellTopBarBackButtonClass: string;
9
+ /** Icon-only control (e.g. overflow menu) in the shell top bar. */
10
+ declare const shellTopBarIconButtonClass: string;
11
+ /**
12
+ * Neutral secondary / outline action in the shell top bar.
13
+ * Uses `!` on hover so shadcn `Button variant="outline"` cannot win.
14
+ */
15
+ declare const shellTopBarSecondaryButtonClass: string;
16
+ /** Primary CTA in the shell top bar (New invoice, Add staff, etc.). */
17
+ declare const shellTopBarPrimaryButtonClass: string;
18
+ /** Positive / payment emphasis in the shell top bar (e.g. Mark as paid). */
19
+ declare const shellTopBarAccentButtonClass: string;
20
+ /** Positive status action in the shell top bar (e.g. Mark resolved). */
21
+ declare const shellTopBarSuccessButtonClass: string;
22
+ /** Informative / reopen action in the shell top bar. */
23
+ declare const shellTopBarInfoButtonClass: string;
24
+ /** Outline-style action — same neutral chrome as {@link shellTopBarSecondaryButtonClass}. */
25
+ declare const shellTopBarOutlineButtonClass: string;
26
+ /** Wrapper for segmented toggles (2D / list, etc.) in the shell top bar. */
27
+ declare const shellTopBarSegmentedShellClass: string;
28
+ declare function shellTopBarSegmentedItemClass(active: boolean): string;
29
+ /** Grouped field shell (month picker, etc.) in the top bar. */
30
+ declare const shellTopBarControlGroupClass: string;
31
+ declare const shellTopBarControlLabelClass = "m-0 shrink-0 text-[11px] font-normal text-sidebar-foreground/60";
32
+ /** Select trigger embedded in {@link shellTopBarControlGroupClass}. */
33
+ declare const shellTopBarSelectTriggerClass = "h-6 min-w-[140px] flex-1 border-0 bg-transparent px-1 text-xs text-sidebar-foreground shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 [&_svg]:text-sidebar-foreground/60";
34
+ /** Square control for date steppers and similar compact actions. */
35
+ declare const shellTopBarCompactIconButtonClass: string;
36
+ /** Center label in a date stepper (shows “Today” or a formatted date). */
37
+ declare const shellTopBarDateLabelButtonClass: string;
38
+
39
+ export { shellTopBarAccentButtonClass, shellTopBarBackButtonClass, shellTopBarCompactIconButtonClass, shellTopBarControlGroupClass, shellTopBarControlLabelClass, shellTopBarDateLabelButtonClass, shellTopBarIconButtonClass, shellTopBarInfoButtonClass, shellTopBarMutedIconClass, shellTopBarNeutralActionDataAttr, shellTopBarOutlineButtonClass, shellTopBarPrimaryButtonClass, shellTopBarSecondaryButtonClass, shellTopBarSegmentedItemClass, shellTopBarSegmentedShellClass, shellTopBarSelectTriggerClass, shellTopBarSuccessButtonClass, shellTopBarTitleTextClass };
@@ -0,0 +1,105 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // src/theme/shell-theme.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ // src/shell/shell-top-bar-chrome.ts
10
+ var shellTopBarMutedIconClass = "text-sidebar-foreground/60";
11
+ var shellTopBarTitleTextClass = "text-sidebar-foreground";
12
+ var shellTopBarActionSize = cn(
13
+ "inline-flex !h-7 min-h-0 items-center justify-center gap-1 px-2.5 text-xs font-medium",
14
+ "[&_svg:not([class*='size-'])]:size-3.5"
15
+ );
16
+ var shellTopBarRadius = "rounded-md";
17
+ var shellTopBarNeutralRest = "border border-sidebar-border bg-sidebar-accent text-sidebar-foreground shadow-none transition-colors";
18
+ var shellTopBarNeutralHover = "hover:!border-sidebar-foreground/14 hover:!bg-sidebar-foreground/6 hover:!text-sidebar-foreground";
19
+ var shellTopBarNeutralActive = "active:!border-sidebar-foreground/10 active:!bg-sidebar-foreground/4 active:!text-sidebar-foreground";
20
+ var shellTopBarNeutralActionDataAttr = {
21
+ "data-shell-top-bar-action": ""
22
+ };
23
+ var shellTopBarBackButtonClass = cn(
24
+ "inline-flex !h-6 !w-6 shrink-0 items-center justify-center text-sidebar-foreground/70",
25
+ shellTopBarRadius,
26
+ shellTopBarNeutralRest,
27
+ shellTopBarNeutralHover,
28
+ shellTopBarNeutralActive
29
+ );
30
+ var shellTopBarIconButtonClass = cn(
31
+ "inline-flex !h-7 !w-7 shrink-0 items-center justify-center text-sidebar-foreground/70",
32
+ shellTopBarRadius,
33
+ shellTopBarNeutralRest,
34
+ shellTopBarNeutralHover,
35
+ shellTopBarNeutralActive,
36
+ "[&_svg:not([class*='size-'])]:size-3.5"
37
+ );
38
+ var shellTopBarSecondaryButtonClass = cn(
39
+ shellTopBarActionSize,
40
+ shellTopBarRadius,
41
+ "!border-sidebar-border !bg-sidebar-accent !text-sidebar-foreground shadow-none transition-colors",
42
+ "!py-0",
43
+ shellTopBarNeutralHover,
44
+ shellTopBarNeutralActive,
45
+ "disabled:pointer-events-none disabled:!border-sidebar-border/60 disabled:!bg-sidebar-accent/60 disabled:!text-sidebar-foreground/40"
46
+ );
47
+ var shellTopBarPrimaryButtonClass = cn(
48
+ shellTopBarActionSize,
49
+ shellTopBarRadius,
50
+ "bg-blue-500 text-white transition-colors hover:bg-blue-600",
51
+ "disabled:pointer-events-none disabled:opacity-50"
52
+ );
53
+ var shellTopBarAccentButtonClass = cn(
54
+ shellTopBarActionSize,
55
+ shellTopBarRadius,
56
+ "bg-emerald-600 text-white transition-colors hover:bg-emerald-700"
57
+ );
58
+ var shellTopBarSuccessButtonClass = cn(
59
+ shellTopBarActionSize,
60
+ shellTopBarRadius,
61
+ "border border-emerald-200 bg-emerald-50 text-emerald-700 transition-colors hover:border-emerald-300 hover:bg-emerald-100"
62
+ );
63
+ var shellTopBarInfoButtonClass = cn(
64
+ shellTopBarActionSize,
65
+ shellTopBarRadius,
66
+ "border border-blue-200 bg-blue-50 text-blue-700 transition-colors hover:border-blue-300 hover:bg-blue-100"
67
+ );
68
+ var shellTopBarOutlineButtonClass = shellTopBarSecondaryButtonClass;
69
+ var shellTopBarSegmentedShellClass = cn(
70
+ "flex items-center gap-0.5 border border-sidebar-border bg-sidebar-accent p-0.5",
71
+ shellTopBarRadius
72
+ );
73
+ function shellTopBarSegmentedItemClass(active) {
74
+ return cn(
75
+ shellTopBarActionSize,
76
+ "!h-6 px-2",
77
+ "rounded-sm",
78
+ active ? "bg-white text-sidebar-foreground shadow-sm" : "text-sidebar-foreground/60 hover:bg-sidebar-foreground/6 hover:text-sidebar-foreground"
79
+ );
80
+ }
81
+ var shellTopBarControlGroupClass = cn(
82
+ "flex h-7 items-center gap-1.5 border border-sidebar-border bg-sidebar-accent pl-2.5 pr-1 text-sidebar-foreground",
83
+ shellTopBarRadius
84
+ );
85
+ var shellTopBarControlLabelClass = "m-0 shrink-0 text-[11px] font-normal text-sidebar-foreground/60";
86
+ var shellTopBarSelectTriggerClass = "h-6 min-w-[140px] flex-1 border-0 bg-transparent px-1 text-xs text-sidebar-foreground shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 [&_svg]:text-sidebar-foreground/60";
87
+ var shellTopBarCompactIconButtonClass = cn(
88
+ "inline-flex !h-6 !w-6 items-center justify-center text-sidebar-foreground/70",
89
+ shellTopBarRadius,
90
+ shellTopBarNeutralRest,
91
+ shellTopBarNeutralHover,
92
+ shellTopBarNeutralActive,
93
+ "[&_svg:not([class*='size-'])]:size-3.5"
94
+ );
95
+ var shellTopBarDateLabelButtonClass = cn(
96
+ shellTopBarActionSize,
97
+ "min-w-[72px]",
98
+ shellTopBarNeutralRest,
99
+ shellTopBarNeutralHover,
100
+ shellTopBarNeutralActive
101
+ );
102
+
103
+ export { shellTopBarAccentButtonClass, shellTopBarBackButtonClass, shellTopBarCompactIconButtonClass, shellTopBarControlGroupClass, shellTopBarControlLabelClass, shellTopBarDateLabelButtonClass, shellTopBarIconButtonClass, shellTopBarInfoButtonClass, shellTopBarMutedIconClass, shellTopBarNeutralActionDataAttr, shellTopBarOutlineButtonClass, shellTopBarPrimaryButtonClass, shellTopBarSecondaryButtonClass, shellTopBarSegmentedItemClass, shellTopBarSegmentedShellClass, shellTopBarSelectTriggerClass, shellTopBarSuccessButtonClass, shellTopBarTitleTextClass };
104
+ //# sourceMappingURL=shell-top-bar-chrome.js.map
105
+ //# sourceMappingURL=shell-top-bar-chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/utils.ts","../src/shell/shell-top-bar-chrome.ts"],"names":[],"mappings":";;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;;;ACFO,IAAM,yBAAA,GAA4B;AAClC,IAAM,yBAAA,GAA4B;AAGzC,IAAM,qBAAA,GAAwB,EAAA;AAAA,EAC5B,uFAAA;AAAA,EACA;AACF,CAAA;AACA,IAAM,iBAAA,GAAoB,YAAA;AAO1B,IAAM,sBAAA,GACJ,sGAAA;AAGF,IAAM,uBAAA,GAEF,mGAAA;AAEJ,IAAM,wBAAA,GAEF,sGAAA;AAGG,IAAM,gCAAA,GAAmC;AAAA,EAC9C,2BAAA,EAA6B;AAC/B;AAGO,IAAM,0BAAA,GAA6B,EAAA;AAAA,EACxC,uFAAA;AAAA,EACA,iBAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA;AACF;AAGO,IAAM,0BAAA,GAA6B,EAAA;AAAA,EACxC,uFAAA;AAAA,EACA,iBAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF;AAMO,IAAM,+BAAA,GAAkC,EAAA;AAAA,EAC7C,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA,kGAAA;AAAA,EACA,OAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF;AAGO,IAAM,6BAAA,GAAgC,EAAA;AAAA,EAC3C,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA,4DAAA;AAAA,EACA;AACF;AAGO,IAAM,4BAAA,GAA+B,EAAA;AAAA,EAC1C,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF;AAGO,IAAM,6BAAA,GAAgC,EAAA;AAAA,EAC3C,qBAAA;AAAA,EACA,iBAAA;AAAA,EAGI;AACN;AAGO,IAAM,0BAAA,GAA6B,EAAA;AAAA,EACxC,qBAAA;AAAA,EACA,iBAAA;AAAA,EAGI;AACN;AAGO,IAAM,6BAAA,GAAgC;AAGtC,IAAM,8BAAA,GAAiC,EAAA;AAAA,EAC5C,gFAAA;AAAA,EACA;AACF;AAEO,SAAS,8BAA8B,MAAA,EAAiB;AAE7D,EAAA,OAAO,EAAA;AAAA,IACL,qBAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA;AAAA,IACA,MAAA,GAGM,4CAAA,GAGA;AAAA,GACR;AACF;AAGO,IAAM,4BAAA,GAA+B,EAAA;AAAA,EAC1C,kHAAA;AAAA,EACA;AACF;AAEO,IAAM,4BAAA,GACX;AAGK,IAAM,6BAAA,GACX;AAGK,IAAM,iCAAA,GAAoC,EAAA;AAAA,EAC/C,8EAAA;AAAA,EACA,iBAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF;AAGO,IAAM,+BAAA,GAAkC,EAAA;AAAA,EAC7C,qBAAA;AAAA,EACA,cAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA;AACF","file":"shell-top-bar-chrome.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { getShellTheme } from \"../theme/shell-theme\";\nimport { cn } from \"../ui/utils\";\n\nexport const shellTopBarMutedIconClass = \"text-sidebar-foreground/60\";\nexport const shellTopBarTitleTextClass = \"text-sidebar-foreground\";\n\n/** Shared sizing and corner radius for shell top bar actions. */\nconst shellTopBarActionSize = cn(\n \"inline-flex !h-7 min-h-0 items-center justify-center gap-1 px-2.5 text-xs font-medium\",\n \"[&_svg:not([class*='size-'])]:size-3.5\",\n);\nconst shellTopBarRadius = \"rounded-md\";\n\nfunction isDarkShell() {\n return getShellTheme() === \"dark\";\n}\n\n/** Resting surface for neutral shell controls (secondary, outline, icon). */\nconst shellTopBarNeutralRest =\n \"border border-sidebar-border bg-sidebar-accent text-sidebar-foreground shadow-none transition-colors\";\n\n/** Visible hover — lighten fill on dark shell, darken on light shell. */\nconst shellTopBarNeutralHover = isDarkShell()\n ? \"hover:!border-sidebar-foreground/25 hover:!bg-sidebar-foreground/12 hover:!text-sidebar-foreground\"\n : \"hover:!border-sidebar-foreground/14 hover:!bg-sidebar-foreground/6 hover:!text-sidebar-foreground\";\n\nconst shellTopBarNeutralActive = isDarkShell()\n ? \"active:!border-sidebar-foreground/20 active:!bg-sidebar-foreground/8 active:!text-sidebar-foreground\"\n : \"active:!border-sidebar-foreground/10 active:!bg-sidebar-foreground/4 active:!text-sidebar-foreground\";\n\n/** Apply to native shell actions (links, ticket buttons) so they share the same hover treatment as {@link ShellTopBarSecondaryActionButton}. */\nexport const shellTopBarNeutralActionDataAttr = {\n \"data-shell-top-bar-action\": \"\",\n} as const;\n\n/** Compact back control in the shell top bar (detail pages). */\nexport const shellTopBarBackButtonClass = cn(\n \"inline-flex !h-6 !w-6 shrink-0 items-center justify-center text-sidebar-foreground/70\",\n shellTopBarRadius,\n shellTopBarNeutralRest,\n shellTopBarNeutralHover,\n shellTopBarNeutralActive,\n);\n\n/** Icon-only control (e.g. overflow menu) in the shell top bar. */\nexport const shellTopBarIconButtonClass = cn(\n \"inline-flex !h-7 !w-7 shrink-0 items-center justify-center text-sidebar-foreground/70\",\n shellTopBarRadius,\n shellTopBarNeutralRest,\n shellTopBarNeutralHover,\n shellTopBarNeutralActive,\n \"[&_svg:not([class*='size-'])]:size-3.5\",\n);\n\n/**\n * Neutral secondary / outline action in the shell top bar.\n * Uses `!` on hover so shadcn `Button variant=\"outline\"` cannot win.\n */\nexport const shellTopBarSecondaryButtonClass = cn(\n shellTopBarActionSize,\n shellTopBarRadius,\n \"!border-sidebar-border !bg-sidebar-accent !text-sidebar-foreground shadow-none transition-colors\",\n \"!py-0\",\n shellTopBarNeutralHover,\n shellTopBarNeutralActive,\n \"disabled:pointer-events-none disabled:!border-sidebar-border/60 disabled:!bg-sidebar-accent/60 disabled:!text-sidebar-foreground/40\",\n);\n\n/** Primary CTA in the shell top bar (New invoice, Add staff, etc.). */\nexport const shellTopBarPrimaryButtonClass = cn(\n shellTopBarActionSize,\n shellTopBarRadius,\n \"bg-blue-500 text-white transition-colors hover:bg-blue-600\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n);\n\n/** Positive / payment emphasis in the shell top bar (e.g. Mark as paid). */\nexport const shellTopBarAccentButtonClass = cn(\n shellTopBarActionSize,\n shellTopBarRadius,\n \"bg-emerald-600 text-white transition-colors hover:bg-emerald-700\",\n);\n\n/** Positive status action in the shell top bar (e.g. Mark resolved). */\nexport const shellTopBarSuccessButtonClass = cn(\n shellTopBarActionSize,\n shellTopBarRadius,\n isDarkShell()\n ? \"border border-emerald-500/35 bg-emerald-500/15 text-emerald-300 transition-colors hover:border-emerald-400/45 hover:bg-emerald-500/25\"\n : \"border border-emerald-200 bg-emerald-50 text-emerald-700 transition-colors hover:border-emerald-300 hover:bg-emerald-100\",\n);\n\n/** Informative / reopen action in the shell top bar. */\nexport const shellTopBarInfoButtonClass = cn(\n shellTopBarActionSize,\n shellTopBarRadius,\n isDarkShell()\n ? \"border border-blue-500/35 bg-blue-500/15 text-blue-300 transition-colors hover:border-blue-400/45 hover:bg-blue-500/25\"\n : \"border border-blue-200 bg-blue-50 text-blue-700 transition-colors hover:border-blue-300 hover:bg-blue-100\",\n);\n\n/** Outline-style action — same neutral chrome as {@link shellTopBarSecondaryButtonClass}. */\nexport const shellTopBarOutlineButtonClass = shellTopBarSecondaryButtonClass;\n\n/** Wrapper for segmented toggles (2D / list, etc.) in the shell top bar. */\nexport const shellTopBarSegmentedShellClass = cn(\n \"flex items-center gap-0.5 border border-sidebar-border bg-sidebar-accent p-0.5\",\n shellTopBarRadius,\n);\n\nexport function shellTopBarSegmentedItemClass(active: boolean) {\n const dark = isDarkShell();\n return cn(\n shellTopBarActionSize,\n \"!h-6 px-2\",\n \"rounded-sm\",\n active\n ? dark\n ? \"bg-sidebar text-sidebar-foreground shadow-sm\"\n : \"bg-white text-sidebar-foreground shadow-sm\"\n : dark\n ? \"text-sidebar-foreground/60 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground\"\n : \"text-sidebar-foreground/60 hover:bg-sidebar-foreground/6 hover:text-sidebar-foreground\",\n );\n}\n\n/** Grouped field shell (month picker, etc.) in the top bar. */\nexport const shellTopBarControlGroupClass = cn(\n \"flex h-7 items-center gap-1.5 border border-sidebar-border bg-sidebar-accent pl-2.5 pr-1 text-sidebar-foreground\",\n shellTopBarRadius,\n);\n\nexport const shellTopBarControlLabelClass =\n \"m-0 shrink-0 text-[11px] font-normal text-sidebar-foreground/60\";\n\n/** Select trigger embedded in {@link shellTopBarControlGroupClass}. */\nexport const shellTopBarSelectTriggerClass =\n \"h-6 min-w-[140px] flex-1 border-0 bg-transparent px-1 text-xs text-sidebar-foreground shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 [&_svg]:text-sidebar-foreground/60\";\n\n/** Square control for date steppers and similar compact actions. */\nexport const shellTopBarCompactIconButtonClass = cn(\n \"inline-flex !h-6 !w-6 items-center justify-center text-sidebar-foreground/70\",\n shellTopBarRadius,\n shellTopBarNeutralRest,\n shellTopBarNeutralHover,\n shellTopBarNeutralActive,\n \"[&_svg:not([class*='size-'])]:size-3.5\",\n);\n\n/** Center label in a date stepper (shows “Today” or a formatted date). */\nexport const shellTopBarDateLabelButtonClass = cn(\n shellTopBarActionSize,\n \"min-w-[72px]\",\n shellTopBarNeutralRest,\n shellTopBarNeutralHover,\n shellTopBarNeutralActive,\n);\n"]}
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ declare function ShellTopBarProvider({ children }: {
5
+ children: ReactNode;
6
+ }): react_jsx_runtime.JSX.Element;
7
+ /** Pages render a title and optional actions for the app shell top bar. */
8
+ declare function ShellTopBar({ title, icon, actions, }: {
9
+ title: ReactNode;
10
+ icon?: ReactNode;
11
+ actions?: ReactNode;
12
+ }): null;
13
+ /** Renders registered page title and actions inside AppWebShell topBar. */
14
+ declare function ShellTopBarFrame(): react_jsx_runtime.JSX.Element;
15
+
16
+ export { ShellTopBar, ShellTopBarFrame, ShellTopBarProvider };
@@ -0,0 +1,71 @@
1
+ import { createContext, useState, useEffect, useSyncExternalStore, useContext } from 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+
4
+ // src/shell/shell-top-bar.tsx
5
+ var EMPTY_TOP_BAR_STATE = {};
6
+ function createShellTopBarStore() {
7
+ let state = EMPTY_TOP_BAR_STATE;
8
+ const listeners = /* @__PURE__ */ new Set();
9
+ return {
10
+ getSnapshot: () => state,
11
+ subscribe: (listener) => {
12
+ listeners.add(listener);
13
+ return () => listeners.delete(listener);
14
+ },
15
+ setState: (next) => {
16
+ state = next;
17
+ listeners.forEach((listener) => listener());
18
+ }
19
+ };
20
+ }
21
+ var ShellTopBarStoreContext = createContext(null);
22
+ function ShellTopBarProvider({ children }) {
23
+ const [store] = useState(createShellTopBarStore);
24
+ return /* @__PURE__ */ jsx(ShellTopBarStoreContext.Provider, { value: store, children });
25
+ }
26
+ function useShellTopBarStore() {
27
+ const store = useContext(ShellTopBarStoreContext);
28
+ if (!store) {
29
+ throw new Error("ShellTopBar components must be used within ShellTopBarProvider");
30
+ }
31
+ return store;
32
+ }
33
+ function ShellTopBar({
34
+ title,
35
+ icon,
36
+ actions
37
+ }) {
38
+ const store = useShellTopBarStore();
39
+ useEffect(() => {
40
+ store.setState({ title, icon, actions });
41
+ return () => store.setState(EMPTY_TOP_BAR_STATE);
42
+ }, [title, icon, actions, store]);
43
+ return null;
44
+ }
45
+ function ShellTopBarFrame() {
46
+ const store = useShellTopBarStore();
47
+ const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
48
+ const { title, icon, actions } = state;
49
+ if (title == null && actions == null) {
50
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center" });
51
+ }
52
+ const titleIsPlainText = typeof title === "string";
53
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
54
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center", children: title != null ? /* @__PURE__ */ jsx(
55
+ "div",
56
+ {
57
+ "data-slot": "shell-top-bar-title",
58
+ className: "flex min-w-0 flex-1 items-center gap-2 text-sidebar-foreground",
59
+ children: titleIsPlainText ? /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ icon != null ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center text-sidebar-foreground/60", children: icon }) : null,
61
+ /* @__PURE__ */ jsx("h1", { className: "truncate text-sm font-normal", children: title })
62
+ ] }) : title
63
+ }
64
+ ) : null }),
65
+ actions != null ? /* @__PURE__ */ jsx("div", { "data-slot": "shell-top-bar-actions", className: "flex shrink-0 items-center gap-1.5", children: actions }) : null
66
+ ] });
67
+ }
68
+
69
+ export { ShellTopBar, ShellTopBarFrame, ShellTopBarProvider };
70
+ //# sourceMappingURL=shell-top-bar.js.map
71
+ //# sourceMappingURL=shell-top-bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shell/shell-top-bar.tsx"],"names":[],"mappings":"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","file":"shell-top-bar.js","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\n\ntype ShellTopBarState = {\n title?: ReactNode;\n icon?: ReactNode;\n actions?: ReactNode;\n};\n\ntype ShellTopBarStore = {\n getSnapshot: () => ShellTopBarState;\n subscribe: (listener: () => void) => () => void;\n setState: (next: ShellTopBarState) => void;\n};\n\nconst EMPTY_TOP_BAR_STATE: ShellTopBarState = {};\n\nfunction createShellTopBarStore(): ShellTopBarStore {\n let state = EMPTY_TOP_BAR_STATE;\n const listeners = new Set<() => void>();\n\n return {\n getSnapshot: () => state,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n setState: (next) => {\n state = next;\n listeners.forEach((listener) => listener());\n },\n };\n}\n\nconst ShellTopBarStoreContext = createContext<ShellTopBarStore | null>(null);\n\nexport function ShellTopBarProvider({ children }: { children: ReactNode }) {\n const [store] = useState(createShellTopBarStore);\n\n return (\n <ShellTopBarStoreContext.Provider value={store}>\n {children}\n </ShellTopBarStoreContext.Provider>\n );\n}\n\nfunction useShellTopBarStore() {\n const store = useContext(ShellTopBarStoreContext);\n if (!store) {\n throw new Error(\"ShellTopBar components must be used within ShellTopBarProvider\");\n }\n return store;\n}\n\n/** Pages render a title and optional actions for the app shell top bar. */\nexport function ShellTopBar({\n title,\n icon,\n actions,\n}: {\n title: ReactNode;\n icon?: ReactNode;\n actions?: ReactNode;\n}) {\n const store = useShellTopBarStore();\n\n useEffect(() => {\n store.setState({ title, icon, actions });\n return () => store.setState(EMPTY_TOP_BAR_STATE);\n }, [title, icon, actions, store]);\n\n return null;\n}\n\n/** Renders registered page title and actions inside AppWebShell topBar. */\nexport function ShellTopBarFrame() {\n const store = useShellTopBarStore();\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n const { title, icon, actions } = state;\n\n if (title == null && actions == null) {\n return <div className=\"flex flex-1 items-center\" />;\n }\n\n const titleIsPlainText = typeof title === \"string\";\n\n return (\n <>\n <div className=\"flex min-w-0 flex-1 items-center\">\n {title != null ? (\n <div\n data-slot=\"shell-top-bar-title\"\n className=\"flex min-w-0 flex-1 items-center gap-2 text-sidebar-foreground\"\n >\n {titleIsPlainText ? (\n <>\n {icon != null ? (\n <span className=\"flex shrink-0 items-center text-sidebar-foreground/60\">{icon}</span>\n ) : null}\n <h1 className=\"truncate text-sm font-normal\">{title}</h1>\n </>\n ) : (\n title\n )}\n </div>\n ) : null}\n </div>\n {actions != null ? (\n <div data-slot=\"shell-top-bar-actions\" className=\"flex shrink-0 items-center gap-1.5\">\n {actions}\n </div>\n ) : null}\n </>\n );\n}\n"]}