+
{
+ const searchRef = React.useRef(null)
+
+ const handleResetFilter = () => {
+ setFilters({
+ source: [],
+ tags: [],
+ errorCode: [],
+ q: "",
+ })
+
+ searchRef.current.value = ""
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ Filter
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const FiltersCombobox = ({
+ filterValues,
+ setFilters,
+ ref,
+ tagOptions = [],
+ sourceOptions = [],
+ errorCodeOptions = [],
+}) => {
+ const { source, errorCode, tags } = filterValues
+ return (
+
+
+ setFilters({ ...filterValues, source: values })
+ }
+ options={sourceOptions}
+ />
+
+
+ setFilters({ ...filterValues, errorCode: values })
+ }
+ options={errorCodeOptions}
+ />
+
+
+ setFilters({ ...filterValues, tags: values })
+ }
+ options={tagOptions}
+ />
+
+ {
+ setFilters({ ...filterValues, q })
+ }}
+ ref={ref}
+ />
+
+ )
+}
+
+const SourceCombobox = ({ values = [], onValueChange, options = [] }) => {
+ const anchor = useComboboxAnchor()
+
+ return (
+
onValueChange?.(values)}
+ >
+
+
+ {(values) => (
+
+ {values.map((value) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ )
+}
+
+const ErrorCodeCombobox = ({ values = [], onValueChange, options = [] }) => {
+ const anchor = useComboboxAnchor()
+
+ return (
+
onValueChange?.(values)}
+ >
+
+
+ {(values) => (
+
+ {values.map((value) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ )
+}
+
+const TagsCodeCombobox = ({ values = [], onValueChange, options = [] }) => {
+ const anchor = useComboboxAnchor()
+
+ return (
+
onValueChange?.(values)}
+ >
+
+
+ {(values) => (
+
+ {values.map((value) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ )
+}
+
+const InputSearch = ({ onSearchChange, ref }) => {
+ const debounceId = React.useRef(null)
+
+ return (
+
+ {
+ if (debounceId) {
+ clearTimeout(debounceId.current)
+ }
+
+ debounceId.current = setTimeout(() => {
+ onSearchChange?.(event.target.value)
+ }, 500)
+ }}
+ />
+
+
+
+
+ )
+}
diff --git a/src/app/troubleshooting/_containers/IssueList.jsx b/src/app/troubleshooting/_containers/IssueList.jsx
new file mode 100644
index 0000000..4582e1a
--- /dev/null
+++ b/src/app/troubleshooting/_containers/IssueList.jsx
@@ -0,0 +1,47 @@
+import Link from "next/link"
+
+export const IssueList = ({ list = [], isLoading }) => {
+ return (
+
+ {!list.length && (
+
No issues found
+ )}
+
+ {!!list.length &&
+ list.map((issue, index) => (
+
+
+
+
+ {issue.title}
+
+
+ {(issue.errorCode || []).join(", ")}
+
+
+
+
+ {[...issue.source, ...issue.tags].map((t, index) => (
+
+ {t}
+
+ ))}
+
+
+
+ {issue.dateTime}
+
+
+
+
+
+ ))}
+
+ )
+}
diff --git a/src/app/troubleshooting/_containers/index.jsx b/src/app/troubleshooting/_containers/index.jsx
new file mode 100644
index 0000000..33ac5d0
--- /dev/null
+++ b/src/app/troubleshooting/_containers/index.jsx
@@ -0,0 +1,79 @@
+"use client"
+
+import { useEffect, useMemo, useState, useTransition } from "react"
+import { Filters } from "./Filters"
+import { IssueList } from "./IssueList"
+
+const TroubleshootingContainer = ({ issues }) => {
+ const [issuesDisplay, setIssuesDisplay] = useState(issues)
+
+ const [filters, setFilters] = useState({
+ source: [],
+ tags: [],
+ errorCode: [],
+ q: "",
+ })
+
+ const { tagOptions, sourceOptions, errorCodeOptions } = useMemo(() => {
+ const tags = new Map()
+ const sources = new Map()
+ const errorCodes = new Map()
+
+ issues.forEach((issue) => {
+ issue.tags.forEach((tag) => {
+ tags.set(tag, tag)
+ })
+
+ issue.source.forEach((source) => {
+ sources.set(source, source)
+ })
+
+ issue.errorCode.forEach((errorCode) => {
+ errorCodes.set(errorCode, errorCode)
+ })
+ })
+
+ return {
+ tagOptions: Array.from(tags.keys()),
+ sourceOptions: Array.from(sources.keys()),
+ errorCodeOptions: Array.from(errorCodes.keys()),
+ }
+ }, [])
+
+ useEffect(() => {
+ const filterEntries = Object.entries(filters).filter(
+ ([key, value]) => value.length > 0,
+ )
+
+ if (!filterEntries.length) {
+ setIssuesDisplay(issues)
+ } else {
+ const issuesFiltered = issues.filter((issue) => {
+ return filterEntries.every(([key, value]) => {
+ if (typeof value !== "string")
+ return value.some((item) => issue[key].includes(item))
+
+ return issue.title.toLowerCase().includes(value.toLowerCase())
+ })
+ })
+
+ setIssuesDisplay(issuesFiltered)
+ }
+ }, [filters])
+
+ return (
+
+
+
+
+
+ )
+}
+
+export default TroubleshootingContainer
diff --git a/src/app/troubleshooting/index.jsx b/src/app/troubleshooting/index.jsx
new file mode 100644
index 0000000..234f279
--- /dev/null
+++ b/src/app/troubleshooting/index.jsx
@@ -0,0 +1,70 @@
+import TroubleshootingContainer from "./_containers"
+import fs from "fs"
+import path from "path"
+import matter from "gray-matter"
+import dayjs from "dayjs"
+import utc from "dayjs/plugin/utc"
+
+dayjs.extend(utc)
+
+export const metadata = {
+ title: "Troubleshooting",
+ description:
+ "Search or browse our troubleshooting guides for solutions to common SpaceDF issues.",
+}
+
+const getTroubleshootingFiles = async () => {
+ const DOCS_DIR = path.join(process.cwd(), "src/app/troubleshooting/issues")
+ const results = []
+
+ const walk = (dir) => {
+ const files = fs.readdirSync(dir)
+ for (const file of files) {
+ const fullPath = path.join(dir, file, "page.mdx")
+
+ const raw = fs.readFileSync(fullPath, "utf-8")
+ const stats = fs.statSync(fullPath)
+
+ const timeUnix = stats.mtimeMs
+ const dateTime = dayjs.utc(timeUnix).format("MMM DD, YYYY")
+
+ if (!raw) continue
+
+ const { data } = matter(raw)
+
+ results.push({
+ title: data?.title || "",
+ source: data?.source || [],
+ errorCode: data.errorCode || [],
+ tags: data?.tags || [],
+ href: `/troubleshooting/issues/${file}`,
+ dateTime,
+ timeUnix,
+ })
+ }
+
+ return results.sort((a, b) => b.timeUnix - a.timeUnix)
+ }
+
+ return {
+ issues: walk(DOCS_DIR),
+ }
+}
+
+export default async function TroubleshootingPage() {
+ const troubleshootingFiles = await getTroubleshootingFiles()
+
+ return (
+
+
+ Troubleshooting
+
+
+ Search or browse our troubleshooting guides for solutions to common
+ SpaceDF issues.
+
+
+
+
+ )
+}
diff --git a/src/app/troubleshooting/issues/google-oauth-redirect-uri-mismatch/page.mdx b/src/app/troubleshooting/issues/google-oauth-redirect-uri-mismatch/page.mdx
new file mode 100644
index 0000000..4b1cf8b
--- /dev/null
+++ b/src/app/troubleshooting/issues/google-oauth-redirect-uri-mismatch/page.mdx
@@ -0,0 +1,105 @@
+---
+title: "Google OAuth: redirect_uri_mismatch"
+source: ["Auth"]
+errorCode: ["401", "500", "unauthenticated", "server_error"]
+tags: ["Self-hosting"]
+---
+import { Steps } from 'nextra/components'
+import {
+ NumberOfContent,
+ NumberWithMdx
+} from "@/app/_components/NumberOfContent.jsx"
+import { CardInfo } from "@/app/_components/CardInfo.jsx"
+import { Callout } from 'nextra/components'
+
+
+# Google OAuth: `redirect_uri_mismatch`
+> This error occurs when the **redirect URI used by SpaceDF does not exactly match** the Authorized Redirect URI configured in the Google Cloud Console.
+
+## Symptoms
+Google shows an error page with:
+
+```text
+Error 400: redirect_uri_mismatch
+```
+
+Google login works in one environment (local or production) but fails in another.
+
+The login flow redirects to Google, then immediately fails.
+
+## Common causes (SpaceDF-specific)
+- `GOOGLE_CALLBACK_URL` in `.env` does not match the redirect URI configured in Google Cloud Console.
+- The redirect URI is correct, but:
+ - Protocol is different (`http` vs `https`)
+ - Port is different (`3000` vs `80`)
+ - Trailing slash mismatch
+- Production domain is not added to Google OAuth settings.
+- Switching from **Quick Start** to **Advanced Setup** without updating OAuth settings.
+
+## Fix
+
+
+### Verify `GOOGLE_CALLBACK_URL`
+Check your .env file:
+
+```bash copy
+# Development
+GOOGLE_CALLBACK_URL=http://localhost:3000
+
+# Production
+GOOGLE_CALLBACK_URL=https://your-domain.com
+```
+
+> Do not include `/auth/google/callback` in `GOOGLE_CALLBACK_URL`
+SpaceDF appends it automatically.
+
+### Update Google Cloud Console
+
+ Go to the [Google Cloud Console](https://console.cloud.google.com/)
+
+
+ Navigate to **APIs & Services → Credentials**
+
+
+ Select your **OAuth 2.0 Client ID**.
+
+
+ Under **Authorized redirect URIs**, add:
+```bash
+# Development
+http://localhost:3000/auth/google/callback
+
+# Production
+https://your-domain.com/auth/google/callback
+```
+> The URI must match exactly, including protocol, domain, port, and path.
+
+
+
+### Restart SpaceDF services
+After updating `.env`, restart all services:
+
+```bash copy
+docker compose down
+docker compose up -d
+```
+
+
+
+ ❌ Using localhost in production
+ ❌ Mixing HTTP and HTTPS
+ ❌ Missing the `/auth/google/callback path` in `Google Console`
+ ❌ Adding trailing slashes inconsistently
+ ❌ Forgetting to restart services after changing `.env`
+
+
+
+***Notes***
+- Google OAuth is **not enabled in Quick Start**.
+- This error only applies when using **Advanced Setup**.
+- Each environment (local, staging, production) requires its own redirect URI entry.
+
\ No newline at end of file
diff --git a/src/app/troubleshooting/issues/rabbitmq-existing-setup-with-different-credentials/page.mdx b/src/app/troubleshooting/issues/rabbitmq-existing-setup-with-different-credentials/page.mdx
new file mode 100644
index 0000000..5f5fd36
--- /dev/null
+++ b/src/app/troubleshooting/issues/rabbitmq-existing-setup-with-different-credentials/page.mdx
@@ -0,0 +1,80 @@
+---
+title: "RabbitMQ: Existing setup with different credentials"
+source: ["RabbitMQ", "Messaging"]
+errorCode: ["ACCESS_REFUSED", "AUTHENTICATION_FAILED"]
+tags: ["Self-hosting", "rabbitmq", "credentials", "volume_conflict", "docker"]
+---
+
+import { Callout } from 'nextra/components'
+import { Steps } from 'nextra/components'
+
+# RabbitMQ: Existing setup with different credentials
+
+> This issue occurs when a **previous RabbitMQ setup already exists** on the machine and was initialized with different credentials than the ones currently configured for SpaceDF.
+
+## Symptoms
+- SpaceDF services fail to start or keep restarting
+- Logs show RabbitMQ authentication errors such as:
+ - `ACCESS_REFUSED`
+ - `authentication failed`
+- Updating `RABBITMQ_DEFAULT_USER` or `RABBITMQ_DEFAULT_PASS` does not fix the issue
+
+## Cause
+RabbitMQ **stores credentials in persistent volumes** on first startup.
+
+If RabbitMQ was previously started with different credentials:
+- Updating environment variables alone is **not enough**
+- RabbitMQ will continue using the **old credentials stored in volumes**
+- This results in a credential mismatch between SpaceDF services and RabbitMQ
+
+## How to verify
+
+Check if an existing RabbitMQ container or volume is present:
+```bash copy
+docker ps -a | grep rabbitmq
+```
+
+```bash copy
+docker volume ls | grep rabbitmq
+```
+If RabbitMQ volumes exist, they may contain credentials from a previous setup.
+
+## Fix (reset RabbitMQ credentials)
+
+
+This will remove all RabbitMQ data.
+Only assume safe in development or fresh setups.
+
+
+
+### Stop all SpaceDF services:
+
+```bash copy
+docker compose down
+```
+
+### Remove existing RabbitMQ volumes:
+```bash copy
+docker volume rm
+```
+
+### Verify credentials in `.env`:
+```bash copy
+RABBITMQ_DEFAULT_USER=your_username
+RABBITMQ_DEFAULT_PASS=your_password
+```
+
+### Restart SpaceDF:
+```bash copy
+docker compose up -d
+```
+
+RabbitMQ will be re-initialized using the new credentials.
+
+
+
+***Notes***
+- Changing RabbitMQ credentials **always requires resetting volumes**
+- Do not reuse RabbitMQ credentials across projects on the same host
+- For production systems, plan credential changes carefully to avoid data loss
+
\ No newline at end of file
diff --git a/src/app/troubleshooting/page.mdx b/src/app/troubleshooting/page.mdx
index fa45a99..2770e4c 100644
--- a/src/app/troubleshooting/page.mdx
+++ b/src/app/troubleshooting/page.mdx
@@ -1,30 +1,8 @@
-# Troubleshooting
-
-Common issues and solutions when working with SpaceDF.
-
-## Getting Started Issues
-
-### Installation Problems
-
-If you're having trouble installing SpaceDF, check:
-- System requirements are met
-- Network connectivity
-- Required permissions
-
-### Connection Issues
-
-Problems connecting to SpaceDF:
-- Verify network settings
-- Check firewall configuration
-- Ensure proper authentication
-
-## Advanced Troubleshooting
-
-For more complex issues, please:
-1. Check the logs
-2. Review configuration files
-3. Contact support through [Community](/community)
-
-## FAQ
-
-Coming soon...
\ No newline at end of file
+---
+title: Troubleshooting
+sidebar: true
+asIndexPage: true
+---
+import TroubleshootingPage from "./index"
+
+
\ No newline at end of file
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000..915ea2a
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,64 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_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",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm": "size-8",
+ "icon-lg": "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps
& {
+ asChild?: boolean
+ }) {
+ const Comp = asChild ? Slot : "button"
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/src/components/ui/combobox.tsx b/src/components/ui/combobox.tsx
new file mode 100644
index 0000000..2f9df6d
--- /dev/null
+++ b/src/components/ui/combobox.tsx
@@ -0,0 +1,310 @@
+"use client"
+
+import * as React from "react"
+import { Combobox as ComboboxPrimitive } from "@base-ui/react"
+import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/components/ui/input-group"
+
+const Combobox = ComboboxPrimitive.Root
+
+function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
+ return
+}
+
+function ComboboxTrigger({
+ className,
+ children,
+ ...props
+}: ComboboxPrimitive.Trigger.Props) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
+ return (
+ }
+ className={cn(className)}
+ {...props}
+ >
+
+
+ )
+}
+
+function ComboboxInput({
+ className,
+ children,
+ disabled = false,
+ showTrigger = true,
+ showClear = false,
+ ...props
+}: ComboboxPrimitive.Input.Props & {
+ showTrigger?: boolean
+ showClear?: boolean
+}) {
+ return (
+
+ }
+ {...props}
+ />
+
+ {showTrigger && (
+
+
+
+ )}
+ {showClear && }
+
+ {children}
+
+ )
+}
+
+function ComboboxContent({
+ className,
+ side = "bottom",
+ sideOffset = 6,
+ align = "start",
+ alignOffset = 0,
+ anchor,
+ ...props
+}: ComboboxPrimitive.Popup.Props &
+ Pick<
+ ComboboxPrimitive.Positioner.Props,
+ "side" | "align" | "sideOffset" | "alignOffset" | "anchor"
+ >) {
+ return (
+
+
+
+
+
+ )
+}
+
+function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
+ return (
+
+ )
+}
+
+function ComboboxItem({
+ className,
+ children,
+ ...props
+}: ComboboxPrimitive.Item.Props) {
+ return (
+
+ {children}
+
+ }
+ >
+
+
+
+ )
+}
+
+function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
+ return (
+
+ )
+}
+
+function ComboboxLabel({
+ className,
+ ...props
+}: ComboboxPrimitive.GroupLabel.Props) {
+ return (
+
+ )
+}
+
+function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
+ return (
+
+ )
+}
+
+function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
+ return (
+
+ )
+}
+
+function ComboboxSeparator({
+ className,
+ ...props
+}: ComboboxPrimitive.Separator.Props) {
+ return (
+
+ )
+}
+
+function ComboboxChips({
+ className,
+ ...props
+}: React.ComponentPropsWithRef &
+ ComboboxPrimitive.Chips.Props) {
+ return (
+
+ )
+}
+
+function ComboboxChip({
+ className,
+ children,
+ showRemove = true,
+ ...props
+}: ComboboxPrimitive.Chip.Props & {
+ showRemove?: boolean
+}) {
+ return (
+
+ {children}
+ {showRemove && (
+ }
+ className="-ml-1 opacity-50 hover:opacity-100"
+ data-slot="combobox-chip-remove"
+ >
+
+
+ )}
+
+ )
+}
+
+function ComboboxChipsInput({
+ className,
+ children,
+ ...props
+}: ComboboxPrimitive.Input.Props) {
+ return (
+
+ )
+}
+
+function useComboboxAnchor() {
+ return React.useRef(null)
+}
+
+export {
+ Combobox,
+ ComboboxInput,
+ ComboboxContent,
+ ComboboxList,
+ ComboboxItem,
+ ComboboxGroup,
+ ComboboxLabel,
+ ComboboxCollection,
+ ComboboxEmpty,
+ ComboboxSeparator,
+ ComboboxChips,
+ ComboboxChip,
+ ComboboxChipsInput,
+ ComboboxTrigger,
+ ComboboxValue,
+ useComboboxAnchor,
+}
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..bbe6fb0
--- /dev/null
+++ b/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,257 @@
+"use client"
+
+import * as React from "react"
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function DropdownMenu({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DropdownMenuPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuContent({
+ className,
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function DropdownMenuGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuItem({
+ className,
+ inset,
+ variant = "default",
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+ variant?: "default" | "destructive"
+}) {
+ return (
+
+ )
+}
+
+function DropdownMenuCheckboxItem({
+ className,
+ children,
+ checked,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuRadioGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuRadioItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuLabel({
+ className,
+ inset,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ )
+}
+
+function DropdownMenuSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuShortcut({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function DropdownMenuSub({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DropdownMenuSubTrigger({
+ className,
+ inset,
+ children,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+function DropdownMenuSubContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ DropdownMenu,
+ DropdownMenuPortal,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubTrigger,
+ DropdownMenuSubContent,
+}
diff --git a/src/components/ui/input-group.tsx b/src/components/ui/input-group.tsx
new file mode 100644
index 0000000..3d1f9d9
--- /dev/null
+++ b/src/components/ui/input-group.tsx
@@ -0,0 +1,170 @@
+"use client"
+
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Textarea } from "@/components/ui/textarea"
+
+function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+ textarea]:h-auto",
+
+ // Variants based on alignment.
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
+
+ // Focus state.
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
+
+ // Error state.
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
+
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+const inputGroupAddonVariants = cva(
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
+ {
+ variants: {
+ align: {
+ "inline-start":
+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
+ "inline-end":
+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
+ "block-start":
+ "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
+ "block-end":
+ "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
+ },
+ },
+ defaultVariants: {
+ align: "inline-start",
+ },
+ }
+)
+
+function InputGroupAddon({
+ className,
+ align = "inline-start",
+ ...props
+}: React.ComponentProps<"div"> & VariantProps
) {
+ return (
+ {
+ if ((e.target as HTMLElement).closest("button")) {
+ return
+ }
+ e.currentTarget.parentElement?.querySelector("input")?.focus()
+ }}
+ {...props}
+ />
+ )
+}
+
+const inputGroupButtonVariants = cva(
+ "text-sm shadow-none flex gap-2 items-center",
+ {
+ variants: {
+ size: {
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
+ sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
+ "icon-xs":
+ "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
+ },
+ },
+ defaultVariants: {
+ size: "xs",
+ },
+ }
+)
+
+function InputGroupButton({
+ className,
+ type = "button",
+ variant = "ghost",
+ size = "xs",
+ ...props
+}: Omit
, "size"> &
+ VariantProps) {
+ return (
+
+ )
+}
+
+function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function InputGroupInput({
+ className,
+ ...props
+}: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+function InputGroupTextarea({
+ className,
+ ...props
+}: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupText,
+ InputGroupInput,
+ InputGroupTextarea,
+}
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..8916905
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
new file mode 100644
index 0000000..3cab5f2
--- /dev/null
+++ b/src/components/ui/popover.tsx
@@ -0,0 +1,89 @@
+"use client"
+
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "@/lib/utils"
+
+function Popover({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverContent({
+ className,
+ align = "center",
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function PopoverAnchor({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
+ return (
+
+ )
+}
+
+function PopoverDescription({
+ className,
+ ...props
+}: React.ComponentProps<"p">) {
+ return (
+
+ )
+}
+
+export {
+ Popover,
+ PopoverTrigger,
+ PopoverContent,
+ PopoverAnchor,
+ PopoverHeader,
+ PopoverTitle,
+ PopoverDescription,
+}
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..7f21b5e
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/src/content/getting-started/self-hosting/docker/advanced-setup.mdx b/src/content/getting-started/self-hosting/docker/advanced-setup.mdx
index 3ef333d..1c5f903 100644
--- a/src/content/getting-started/self-hosting/docker/advanced-setup.mdx
+++ b/src/content/getting-started/self-hosting/docker/advanced-setup.mdx
@@ -1,10 +1,11 @@
import { Steps } from "nextra/components"
import { Table } from 'nextra/components'
import { Callout } from 'nextra/components'
+import { CardInfo } from "@/app/_components/CardInfo.jsx"
import {
- NumberOfContent,
- NumberWithMdx
+ NumberOfContent,
+ NumberWithMdx
} from "@/app/_components/NumberOfContent.jsx"
@@ -12,15 +13,12 @@ import {
> This guide covers **detailed configuration**, **customization**, **and operational topics** for running SpaceDF with Docker Compose.
-
-
Use this guide if you need:
-
- - Production-ready configuration
- - Security hardening
- - Custom ports, domains, or authentication
- - Update, maintenance, and removal procedures
-
-
+
+ - Production-ready configuration
+ - Security hardening
+ - Custom ports, domains, or authentication
+ - Update, maintenance, and removal procedures
+
If you only want to get SpaceDF running quickly with default settings, see [Quick Start](/docs/getting-started/self-hosting/docker/quick-start).
---
@@ -129,6 +127,18 @@ You must replace these values before starting SpaceDF in a self-hosted environme
Review the configuration options below and make sure all secret values are set before starting SpaceDF.
+
+***Security notes***
+- **Do not commit** passwords, secrets, or API keys to Git
+- **Do not expose secret values** on the client side (browser)
+- If a value is marked as **“Keep this value secret”**, it must never be shared publicly
+- **Do not reuse secrets** across systems or environments
+- Use strong and unique passwords for all services
+- Use the **minimum required permissions** for all credentials
+- **Rotate keys immediately** if they are **exposed**
+- Use **HTTPS / WSS** in production environments
+
+
### Configuring Environment Variables
This section explains how to configure the required environment variables in the `.env` file before starting SpaceDF.
@@ -160,6 +170,7 @@ RABBITMQ_DEFAULT_PASS=your_password
Keep these credentials safe—you may need them to log in to RabbitMQ for troubleshooting later.
+---
##### Authentication (JWT)
> SpaceDF uses JSON Web Tokens (JWT) to authenticate users and secure API requests.
@@ -169,8 +180,8 @@ You must set a **private key** and a **public key** before starting SpaceDF.
**Recommended:** Generate a new key pair
```bash copy
-openssl genrsa -out jwt_private.pem 2048
-openssl rsa -in jwt_private.pem -pubout -out jwt_public.pem
+openssl genrsa -out private_key.pem 2048
+openssl rsa -in private_key.pem -pubout -out public_key.pem
```
Copy the contents of each file into your `.env` file:
@@ -183,14 +194,7 @@ JWT_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----...
- `JWT_PUBLIC_KEY` - Verifies authentication tokens. This key can be shared with other services if needed.
> Make sure the keys are pasted correctly and not broken across lines.
-
-
- **Security notes**
- - Do not commit JWT private keys to Git
- - Do not reuse keys from other systems
- - Rotate keys if they are exposed
-
-
+---
##### Google OAuth
> Google OAuth allows users to sign in to SpaceDF using their Google account.
@@ -237,21 +241,17 @@ GOOGLE_CLIENT_SECRET=your_google_client_secret #(Step 5)
- `GOOGLE_CALLBACK_URL` - The URL Google redirects users back to after successful login.
- `GOOGLE_CLIENT_ID` - Identifies your application to Google.
- `GOOGLE_CLIENT_SECRET` - A private key used by SpaceDF to securely communicate with Google (***Keep this value secret.***)
-
-
- **Security notes**
- - Do not commit `GOOGLE_CLIENT_SECRET` to Git
- - Use HTTPS for the callback URL in production
- - Rotate the secret if it is exposed
+
+Use HTTPS for the callback URL in production.
-
+---
##### Apple OAuth (Coming Soon)
Apple sign-in support is planned but not yet supported in SpaceDF.
Do not configure these values yet.
- Apple OAuth is not supported in the current release.
+ Apple OAuth is **not supported** in the current release.
```bash copy
@@ -261,7 +261,7 @@ APPLE_CLIENT_SECRET=__APPLE_CLIENT_SECRET__
APPLE_CLIENT_KEY=__APPLE_CLIENT_KEY__
APPLE_CERTIFICATE_KEY=__APPLE_CERTIFICATE_KEY__
```
-
+---
##### Auth Service
> The Auth Service is responsible for user authentication, authorization, and tenant management in SpaceDF.
@@ -294,14 +294,7 @@ Set this to the domain or host where SpaceDF will be accessed:
```bash copy
DEFAULT_TENANT_HOST=app.spacedf.example
```
-
-
- **Security notes**
- - Do not commit secrets or API keys to Git
- - Do not reuse secrets from other systems
- - Rotate keys if they are exposed
-
-
+---
##### S3 Service
> The Amazon S3 service is used by SpaceDF to store files such as uploads, assets, and generated data.
@@ -309,19 +302,15 @@ DEFAULT_TENANT_HOST=app.spacedf.example
SpaceDF supports **Amazon S3 only** for file storage in this setup.
-**How it works**
-
SpaceDF connects to Amazon S3 using **IAM credentials** that you provide in the `.env` file. All file operations (upload, read, delete) are handled internally by SpaceDF services.
End users do **not** need AWS accounts or credentials.
-**How to configure**
-
If you do not already have an AWS account, create one at, [see more](https://repost.aws/knowledge-center/create-and-activate-aws-account):
https://signin.aws.amazon.com/signup?request_type=register
After creating your account, sign in to the AWS Management Console to continue with the steps below.
-***Create an S3 bucket***
+**Create an S3 bucket**
Open the [Amazon S3 Console](https://console.aws.amazon.com/s3/)
@@ -340,7 +329,7 @@ Save the bucket name and region for later use.
Read more: [AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html#creating-bucket)
-***Create IAM credentials***
+**Create IAM credentials**
Open the [AWS IAM Console](https://console.aws.amazon.com/iam/)
@@ -359,7 +348,11 @@ Read more: [AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/userg
Read more: [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
-***Set values in `.env`***
+
+Use IAM policies with **minimum required permissions**.
+
+
+**Set values in `.env`**
```bash copy
# Replace with your own values.
AWS_ACCESS_KEY_ID=AKIAXXXXXXXX
@@ -373,14 +366,7 @@ Read more: [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/
- `AWS_SECRET_ACCESS_KEY` - The secret key paired with the access key above. (***Keep this value private***)
- `AWS_STORAGE_BUCKET_NAME` - The name of the S3 bucket where SpaceDF stores files.
- `AWS_REGION` - The AWS region where the S3 bucket is located. (e.g., `us-east-1`, `ap-southeast-1`).
-
-
- **Security notes**
- - Do not commit AWS credentials to Git
- - Use IAM policies with **minimum required permissions**
- - Rotate access keys if they are exposed
-
-
+---
##### Redis
> Redis is used by SpaceDF for caching and fast data access.
@@ -395,19 +381,26 @@ REDIS_HOST="redis://redis:6379/1"
- `6379` — Default Redis port
- `/1` — Redis database number
-
- **When you do NOT need to change this**
- - You are using the provided Docker setup
- - Redis is running as part of the included Docker Compose file
-
-
-
- **When you SHOULD change this**
- - Redis runs on a different server or host
- - Redis uses a non-default port
- - You want to use a different Redis database
-
+
+ - You are using the provided Docker setup
+ - Redis is running as part of the included Docker Compose file
+
+
+
+ - Redis runs on a different server or host
+ - Redis uses a non-default port
+ - You want to use a different Redis database
+
+---
##### Dashboard Service
> The Dashboard Service provides the web interface for managing SpaceDF, including administration and monitoring features.
@@ -432,13 +425,7 @@ DASHBOARD_SECRET_KEY=generated_secret_value
```
`DASHBOARD_SECRET_KEY` - A secret key used to sign and protect dashboard-related sessions and data. (***Keep this value private.***)
-
- **Security notes**
- - Do not commit passwords or secret keys to Git
- - Do not reuse secrets from other services
- - Rotate keys if they are exposed
-
-
+---
##### Device Service
> The Device Service manages devices, device data, and communication with telemetry-related services in SpaceDF.
@@ -465,12 +452,6 @@ DEVICE_SECRET_KEY=generated_secret_value
```
`DEVICE_SECRET_KEY` - A secret key used to sign and protect device-related data and requests. (***Keep this value private.***)
-
- **Security notes**
- - Do not commit passwords or secret keys to Git
- - Rotate secrets if they are exposed
-
-
**Telemetry service URL**
Defines the internal Docker service address that SpaceDF uses to communicate with the Telemetry Service.
@@ -481,11 +462,18 @@ When using the **default Docker setup**, the Telemetry Service runs as a Docker
# Default (recommended)
TELEMETRY_SERVICE_URL=http://telemetry-service:8080
```
-- This value is the same for both **local and production** deployments when using Docker Compose.
-- The Telemetry Service runs from a **Docker image** and is exposed internally.
-- You **do not need to change this value** in most cases.
+
+ - This value is the same for both **local and production** deployments when using Docker Compose.
+ - The Telemetry Service runs from a **Docker image** and is exposed internally.
+ - You **do not need to change this value** in most cases.
+
Only change this value if you modify the Telemetry Service name or port in `docker-compose.yml`.
+
+---
##### EMQX Service
> EMQX is the MQTT broker used by SpaceDF to handle device messaging and real-time communication.
@@ -501,13 +489,7 @@ EMQX_PASSWORD=change_this_to_a_secure_password
- `EMQX_USERNAME` - The username SpaceDF uses to authenticate with the EMQX broker.
- `EMQX_PASSWORD` - The password for the EMQX user above. (***Use a strong and unique password***.)
-
- **Security notes**
- - Do not commit EMQX credentials to Git
- - Do not reuse broker credentials across environments
- - Rotate credentials if they are exposed
-
-
+---
##### Broker Bridge Service
> The Broker Bridge Service connects SpaceDF to an external MQTT broker or bridges messages between brokers.
@@ -530,14 +512,7 @@ MQTT_TOPICS=device/+/telemetry,device/+/status
```
`MQTT_TOPICS` - A list of MQTT topics that SpaceDF subscribes to or bridges.
-
- **Security notes**
- - Do not commit MQTT credentials to Git
- - Limit broker permissions to required topics only
- - Rotate credentials if they are exposed
-
-
-
+---
##### AWS to access to SES service
> SpaceDF uses email services to send system emails such as account verification, password resets, and notifications.
@@ -572,29 +547,18 @@ This setup commonly uses **AWS Simple Email Service (SES)**, but can be adapted
-
- Find your SMTP endpoint and set it as `EMAIL_HOST`.
-
Set the following values in your `.env` file.
```bash copy
# Replace with your own values.
-EMAIL_BACKEND=ses
-EMAIL_HOST=email-smtp.us-east-1.amazonaws.com
-EMAIL_PORT=587
-EMAIL_USE_TLS=true
EMAIL_HOST_USER=AKIAXXXXXXXX
EMAIL_HOST_PASSWORD=XXXXXXXXXXXXXXXX
DEFAULT_FROM_EMAIL=no-reply@spacedf.example
```
-- `EMAIL_BACKEND` - Specifies the email provider. Use `ses` when sending email through AWS SES.
-- `EMAIL_HOST` - The SMTP endpoint provided by AWS SES (e.g. `email-smtp.us-east-1.amazonaws.com`).
-- `EMAIL_PORT` - The SMTP port used to send email. Common values: `587` (TLS) or `465` (SSL).
-- `EMAIL_USE_TLS` - Enables secure email delivery using TLS. Recommended value: `true`.
- `EMAIL_HOST_USER` - The SMTP username generated by AWS SES.
- `EMAIL_HOST_PASSWORD` - The SMTP password generated by AWS SES (***Keep this value secret***)
- `DEFAULT_FROM_EMAIL` - The sender email address shown to users.
-
+---
##### MPA Service
> The MPA Service connects SpaceDF to an MQTT broker to receive and publish messages for application-level processing.
@@ -615,13 +579,11 @@ MQTT_TOPIC=devices/+/events
- `MQTT_CLIENT_ID` - A unique client identifier for the MPA Service when connecting to MQTT.
- `MQTT_TOPIC` - The MQTT topic the MPA Service subscribes to.
-
- **Security notes**
- - Do not commit MQTT credentials to Git
- - Use TLS (`8883`) in production if available
- - Restrict broker permissions to required topics only
+
+ Use TLS (`8883`) in production if available
+ Restrict broker permissions to required topics only
-
+---
##### Bootstrap Service
> The Bootstrap Service is the **backend service** responsible for initial system setup and cross-service coordination when SpaceDF starts.
@@ -642,9 +604,14 @@ HOST=https://api.spacedf.example
`HOST` - The public base URL where the Bootstrap Service is accessible. This is used by other services to communicate with it.
-If you change the backend port, make sure to update:
-- HOST in your `.env` file
-- The backend port in `docker-compose.yml`
+
+ - HOST in your `.env` file
+ - The backend port in `docker-compose.yml`
+
**CORS origins**
@@ -665,7 +632,21 @@ CORS_ALLOWED_ORIGINS=https://app.spacedf.example,https://admin.spacedf.example
`CORS_ALLOWED_ORIGINS` - A comma-separated list of allowed origins for cross-origin requests. This controls which frontend domains can access the Bootstrap Service.
-> Only include domains you trust. Avoid using wildcards (*) in production.
+
+ **Important notes**
+ - Controls **which frontend domains can access** the Bootstrap service
+ - Each frontend **must be listed explicitly**, including protocol and port
+ - If you change frontend ports in `docker-compose.yml`, update both the Docker ports and `CORS_ALLOWED_ORIGINS`
+
+
+Example:
+
+If the Dashboard is moved from port `3000` to `4000`, you must add:
+
+```text copy
+http://localhost:4000
+```
+to `CORS_ALLOWED_ORIGINS`.
**Secret key**: Generate a secure random value:
```bash
@@ -680,6 +661,7 @@ BOOTSTRAP_SECRET_KEY=generated_secret_value
```
`BOOTSTRAP_SECRET_KEY` - A secret key used to secure internal Bootstrap operations. (***Keep this value private.***)
+---
##### Organization Initialization
> These settings are used to create the **initial organization and owner account** when SpaceDF starts for the first time. This step runs only during the first startup.
@@ -696,20 +678,19 @@ OWNER_PASSWORD=change_this_to_a_secure_password
- `OWNER_EMAIL` - The email address of the initial organization owner.
- `OWNER_PASSWORD` - The password for the owner account. (***Use a strong and secure password***.)
-
- **Security notes**
- - Change the owner password after first login if required
- - Do not commit owner credentials to Git
- - Use a real email address to receive system notifications
-
-
-You have now finished configuring the backend services.
-Next, continue with the frontend (web app) configuration.
+---
+🎉 You have now finished configuring the backend services.
+Next, continue with the frontend (web app) configuration.
+---
### Admin Portal Configuration
-The Admin Portal is used for **system-level** and **organization-level** administration.
-
-This application is typically accessed by **platform administrators**, not end users.
+
+ The Admin Portal is used for **system-level** and **organization-level** administration.
+ This application is typically accessed by **platform administrators**, not end users.
+
Configure these environment variables to connect the Admin Portal to backend services and authentication providers.
@@ -718,7 +699,6 @@ Configure these environment variables to connect the Admin Portal to backend ser
Make sure you have completed the **Backend Services Configuration** above.
The Admin Portal depends on backend authentication and API services.
-
##### Authentication
**Generate NextAuth secret**
@@ -749,7 +729,7 @@ PORTAL_NEXTAUTH_URL=https://admin.spacedf.example
- `HOST_FRONTEND_ADMIN` - The public URL where the Admin Portal is accessible.
- `PORTAL_NEXTAUTH_URL` - The base URL used by NextAuth for redirects and callbacks. This value should match the Admin Portal public URL.
-
+---
##### API Access
**Using Docker**
@@ -766,20 +746,14 @@ PORTAL_AUTH_API=https://api.spacedf.example
`PORTAL_AUTH_API` - The backend API endpoint used by the Admin Portal for authentication.
This typically points to the internal API gateway or load balancer.
-
- **Security notes**
- - Do not commit `PORTAL_NEXTAUTH_SECRET` to Git
- - Always use HTTPS in production
- - Restrict access to the Admin Portal to trusted users only
-
-
+---
##### References
Use the official documentation below if you need more details about **NextAuth** behavior or advanced configuration:
- [NextAuth.js Documentation](https://next-auth.js.org/)
- [NextAuth Environment Variables](https://next-auth.js.org/configuration/options)
- [NextAuth Security Guide](https://next-auth.js.org/security)
-
+---
### Dashboard Configuration
This section configures the **SpaceDF Dashboard**, the main web interface used for daily operations, device monitoring, and data visualization.
@@ -807,7 +781,7 @@ DASHBOARD_NEXTAUTH_URL=http://localhost:3000
- `DASHBOARD_NEXTAUTH_SECRET` - A secret key used by NextAuth to encrypt sessions and tokens. (***Keep this value private.***)
- `DASHBOARD_NEXTAUTH_URL` - The public URL where the Dashboard is accessible.
This value is used for redirects and callbacks.
-
+---
##### API Access
**Using Docker**
@@ -820,15 +794,10 @@ DASHBOARD_AUTH_API=http://haproxy:3000
# Replace with your own values.
DASHBOARD_AUTH_API=https://api.spacedf.example
```
-
+---
##### Map Services
> The Dashboard uses **MapTiler** as a map service to resolve location names from coordinates and support location-based features.
-```bash copy
-# Replace with your API Key.
-MAPTILER_API_KEY=__MAPTILER_API_KEY__
-```
-
**How to get a MapTiler API key**
Create a MapTiler account: [MapTiler](https://www.maptiler.com/)
@@ -840,6 +809,11 @@ MAPTILER_API_KEY=__MAPTILER_API_KEY__
Copy the key and set it as `MAPTILER_API_KEY`.
+```bash copy
+# Replace with your API Key.
+MAPTILER_API_KEY=__MAPTILER_API_KEY__
+```
+---
##### MQTT (Real-time Data)
> The Dashboard connects to MQTT to receive real-time device data.
@@ -865,13 +839,11 @@ Use ws (WebSocket) for browser-based clients.
- `DASHBOARD_MQTT_PORT` - Port exposed by the MQTT broker for WebSocket connections.
- `DASHBOARD_MQTT_BROKER` - Host and port of the MQTT broker accessible from the browser.
-
- **Security notes**
- - Do not commit secrets or API keys to Git
- - Use HTTPS and `wss` in production environments
- - Restrict MQTT permissions to read-only topics for the Dashboard
+
+ Use HTTPS and `wss` in production environments
+ Restrict MQTT permissions to read-only topics for the Dashboard
-
+---
##### References
- [NextAuth.js Documentation](https://next-auth.js.org/)
- [MapTiler Documentation](https://docs.maptiler.com/)
diff --git a/src/content/getting-started/self-hosting/docker/quick-start.mdx b/src/content/getting-started/self-hosting/docker/quick-start.mdx
index 5faa8ce..d6e5be0 100644
--- a/src/content/getting-started/self-hosting/docker/quick-start.mdx
+++ b/src/content/getting-started/self-hosting/docker/quick-start.mdx
@@ -3,23 +3,33 @@ sidebarTitle: Quick Start
---
import { Steps } from 'nextra/components'
+import { CardInfo } from "@/app/_components/CardInfo.jsx"
+
# Quick Start
> This guide helps you get **SpaceDF running quickly** on your own infrastructure using **Docker Compose** and **default settings**.
-
-
Before You Begin
-
- Make sure you have:
+
+
+ ❌ Google login is not enabled
+ ❌ AWS integrations (S3, SES) are not configured
+ ❌ Email sending is disabled
+ ❌ Image uploads and asset images are not displayed (cloud storage is not configured)
+
+
+
+If you need advanced configuration, security tuning, or production setup, see [Advanced Setup](/docs/getting-started/self-hosting/docker/advanced-setup) instead.
+
+
+
+ Make sure you have:
- Docker and Docker Compose installed
- Basic Linux command-line knowledge
- An environment with at least the minimum system requirements
-
- If you are unsure about any of the requirements above, see [Prerequisites](/docs/getting-started/self-hosting/docker/advanced-setup#prerequisites) for details.
-
-
-If you need advanced configuration, security tuning, or production setup, see [Advanced Setup](/docs/getting-started/self-hosting/docker/advanced-setup) instead.
+
+ If you are unsure about any of the requirements above, see [Prerequisites](/docs/getting-started/self-hosting/docker/advanced-setup#prerequisites) for details.
+
### Get the Source Code
@@ -106,8 +116,89 @@ After startup, SpaceDF services are available on the following default ports:
Make sure these ports are open on your server and not used by other services.
-🎉 **You’re all set!** SpaceDF is now running on your server. Enjoy building with it.
+### Default account (Quick Start only)
+> Quick Start creates default accounts for initial access
+
+These accounts are for **local testing only**.
+
+#### Frontend Web Apps
+
+
+- Username: `admin@example.com`
+- Password: `changeme@Default123`
+
+
+
+- Username: `admin@example.com`
+- Password: `changeme@Default123`
+
+
+#### Backend Services
+
+
+- Host: `localhost:5434`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `auth_service`
+
+
+
+- Host: `localhost:5433`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `bootstrap_service`
+
+
+
+- Host: `localhost:5435`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `dashboard_service`
+
+
+
+- Host: `localhost:5436`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `device_service`
+
+
+
+- Host: `localhost:5437`
+- Username: `postgres`
+- Password: `postgres`
+- Database: `spacedf_telemetry`
+
+
+To configure authentication, user management, and production security,
+continue with [Advanced Setup](/docs/getting-started/self-hosting/docker/advanced-setup).
----
-
-Continue with [Advanced Setup](/docs/getting-started/self-hosting/docker/advanced-setup) to customize your deployment.
+🎉 **You’re all set!** SpaceDF is now running on your server. Enjoy building with it.
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/tsconfig.json b/tsconfig.json
index b9dc67c..a4a833b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -32,7 +32,9 @@
"**/*.tsx",
"src/app/_components/OptionsCard.tsx",
"src/app/_components/RichCard.jsx",
- "src/app/_components/NumberOfContent.jsx"
+ "src/app/_components/NumberOfContent.jsx",
+ "src/app/_components/CardInfo.jsx",
+ "src/app/troubleshooting/_containers/index.jsx"
],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index a0f5d81..cf42167 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15,6 +15,34 @@
package-manager-detector "^1.3.0"
tinyexec "^1.0.1"
+"@babel/runtime@^7.28.4":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b"
+ integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
+
+"@base-ui/react@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@base-ui/react/-/react-1.1.0.tgz#2afb4508049c711c74df9f3003148ed2e381a35b"
+ integrity sha512-ikcJRNj1mOiF2HZ5jQHrXoVoHcNHdBU5ejJljcBl+VTLoYXR6FidjTN86GjO6hyshi6TZFuNvv0dEOgaOFv6Lw==
+ dependencies:
+ "@babel/runtime" "^7.28.4"
+ "@base-ui/utils" "0.2.4"
+ "@floating-ui/react-dom" "^2.1.6"
+ "@floating-ui/utils" "^0.2.10"
+ reselect "^5.1.1"
+ tabbable "^6.4.0"
+ use-sync-external-store "^1.6.0"
+
+"@base-ui/utils@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@base-ui/utils/-/utils-0.2.4.tgz#6ba65d357e0fc7e5a7748cfccadb322d5af7f5d2"
+ integrity sha512-smZwpMhjO29v+jrZusBSc5T+IJ3vBb9cjIiBjtKcvWmRj9Z4DWGVR3efr1eHR56/bqY5a4qyY9ElkOY5ljo3ng==
+ dependencies:
+ "@babel/runtime" "^7.28.4"
+ "@floating-ui/utils" "^0.2.10"
+ reselect "^5.1.1"
+ use-sync-external-store "^1.6.0"
+
"@braintree/sanitize-url@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz#15e19737d946559289b915e5dad3b4c28407735e"
@@ -81,6 +109,13 @@
dependencies:
"@floating-ui/utils" "^0.2.10"
+"@floating-ui/core@^1.7.4":
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.4.tgz#4a006a6e01565c0f87ba222c317b056a2cffd2f4"
+ integrity sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==
+ dependencies:
+ "@floating-ui/utils" "^0.2.10"
+
"@floating-ui/dom@^1.7.4":
version "1.7.4"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.4.tgz#ee667549998745c9c3e3e84683b909c31d6c9a77"
@@ -89,7 +124,22 @@
"@floating-ui/core" "^1.7.3"
"@floating-ui/utils" "^0.2.10"
-"@floating-ui/react-dom@^2.1.2":
+"@floating-ui/dom@^1.7.5":
+ version "1.7.5"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.5.tgz#60bfc83a4d1275b2a90db76bf42ca2a5f2c231c2"
+ integrity sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==
+ dependencies:
+ "@floating-ui/core" "^1.7.4"
+ "@floating-ui/utils" "^0.2.10"
+
+"@floating-ui/react-dom@^2.0.0":
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.7.tgz#529475cc16ee4976ba3387968117e773d9aa703e"
+ integrity sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==
+ dependencies:
+ "@floating-ui/dom" "^1.7.5"
+
+"@floating-ui/react-dom@^2.1.2", "@floating-ui/react-dom@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.6.tgz#189f681043c1400561f62972f461b93f01bf2231"
integrity sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==
@@ -574,6 +624,252 @@
resolved "https://registry.yarnpkg.com/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz#ba68fd609621132e8e314a89e2d2d52516f61723"
integrity sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==
+"@radix-ui/primitive@1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba"
+ integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==
+
+"@radix-ui/react-arrow@1.1.7":
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz#e14a2657c81d961598c5e72b73dd6098acc04f09"
+ integrity sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==
+ dependencies:
+ "@radix-ui/react-primitive" "2.1.3"
+
+"@radix-ui/react-collection@1.1.7":
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.7.tgz#d05c25ca9ac4695cc19ba91f42f686e3ea2d9aec"
+ integrity sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-slot" "1.2.3"
+
+"@radix-ui/react-compose-refs@1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30"
+ integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==
+
+"@radix-ui/react-context@1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36"
+ integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==
+
+"@radix-ui/react-direction@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14"
+ integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==
+
+"@radix-ui/react-dismissable-layer@1.1.11":
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37"
+ integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==
+ dependencies:
+ "@radix-ui/primitive" "1.1.3"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+ "@radix-ui/react-use-escape-keydown" "1.1.1"
+
+"@radix-ui/react-dropdown-menu@^2.1.16":
+ version "2.1.16"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz#5ee045c62bad8122347981c479d92b1ff24c7254"
+ integrity sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==
+ dependencies:
+ "@radix-ui/primitive" "1.1.3"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-id" "1.1.1"
+ "@radix-ui/react-menu" "2.1.16"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-controllable-state" "1.2.2"
+
+"@radix-ui/react-focus-guards@1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz#2a5669e464ad5fde9f86d22f7fdc17781a4dfa7f"
+ integrity sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==
+
+"@radix-ui/react-focus-scope@1.1.7":
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz#dfe76fc103537d80bf42723a183773fd07bfb58d"
+ integrity sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+
+"@radix-ui/react-id@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7"
+ integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==
+ dependencies:
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/react-menu@2.1.16":
+ version "2.1.16"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.16.tgz#528a5a973c3a7413d3d49eb9ccd229aa52402911"
+ integrity sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==
+ dependencies:
+ "@radix-ui/primitive" "1.1.3"
+ "@radix-ui/react-collection" "1.1.7"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-direction" "1.1.1"
+ "@radix-ui/react-dismissable-layer" "1.1.11"
+ "@radix-ui/react-focus-guards" "1.1.3"
+ "@radix-ui/react-focus-scope" "1.1.7"
+ "@radix-ui/react-id" "1.1.1"
+ "@radix-ui/react-popper" "1.2.8"
+ "@radix-ui/react-portal" "1.1.9"
+ "@radix-ui/react-presence" "1.1.5"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-roving-focus" "1.1.11"
+ "@radix-ui/react-slot" "1.2.3"
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+ aria-hidden "^1.2.4"
+ react-remove-scroll "^2.6.3"
+
+"@radix-ui/react-popover@^1.1.15":
+ version "1.1.15"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.15.tgz#9c852f93990a687ebdc949b2c3de1f37cdc4c5d5"
+ integrity sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.3"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-dismissable-layer" "1.1.11"
+ "@radix-ui/react-focus-guards" "1.1.3"
+ "@radix-ui/react-focus-scope" "1.1.7"
+ "@radix-ui/react-id" "1.1.1"
+ "@radix-ui/react-popper" "1.2.8"
+ "@radix-ui/react-portal" "1.1.9"
+ "@radix-ui/react-presence" "1.1.5"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-slot" "1.2.3"
+ "@radix-ui/react-use-controllable-state" "1.2.2"
+ aria-hidden "^1.2.4"
+ react-remove-scroll "^2.6.3"
+
+"@radix-ui/react-popper@1.2.8":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.8.tgz#a79f39cdd2b09ab9fb50bf95250918422c4d9602"
+ integrity sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==
+ dependencies:
+ "@floating-ui/react-dom" "^2.0.0"
+ "@radix-ui/react-arrow" "1.1.7"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+ "@radix-ui/react-use-rect" "1.1.1"
+ "@radix-ui/react-use-size" "1.1.1"
+ "@radix-ui/rect" "1.1.1"
+
+"@radix-ui/react-portal@1.1.9":
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472"
+ integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==
+ dependencies:
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/react-presence@1.1.5":
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db"
+ integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/react-primitive@2.1.3":
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz#db9b8bcff49e01be510ad79893fb0e4cda50f1bc"
+ integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==
+ dependencies:
+ "@radix-ui/react-slot" "1.2.3"
+
+"@radix-ui/react-roving-focus@1.1.11":
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz#ef54384b7361afc6480dcf9907ef2fedb5080fd9"
+ integrity sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==
+ dependencies:
+ "@radix-ui/primitive" "1.1.3"
+ "@radix-ui/react-collection" "1.1.7"
+ "@radix-ui/react-compose-refs" "1.1.2"
+ "@radix-ui/react-context" "1.1.2"
+ "@radix-ui/react-direction" "1.1.1"
+ "@radix-ui/react-id" "1.1.1"
+ "@radix-ui/react-primitive" "2.1.3"
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+ "@radix-ui/react-use-controllable-state" "1.2.2"
+
+"@radix-ui/react-slot@1.2.3":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1"
+ integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.2"
+
+"@radix-ui/react-slot@^1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.4.tgz#63c0ba05fdf90cc49076b94029c852d7bac1fb83"
+ integrity sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.2"
+
+"@radix-ui/react-use-callback-ref@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40"
+ integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==
+
+"@radix-ui/react-use-controllable-state@1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190"
+ integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==
+ dependencies:
+ "@radix-ui/react-use-effect-event" "0.0.2"
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/react-use-effect-event@0.0.2":
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907"
+ integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==
+ dependencies:
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/react-use-escape-keydown@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29"
+ integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==
+ dependencies:
+ "@radix-ui/react-use-callback-ref" "1.1.1"
+
+"@radix-ui/react-use-layout-effect@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e"
+ integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==
+
+"@radix-ui/react-use-rect@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz#01443ca8ed071d33023c1113e5173b5ed8769152"
+ integrity sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==
+ dependencies:
+ "@radix-ui/rect" "1.1.1"
+
+"@radix-ui/react-use-size@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz#6de276ffbc389a537ffe4316f5b0f24129405b37"
+ integrity sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==
+ dependencies:
+ "@radix-ui/react-use-layout-effect" "1.1.1"
+
+"@radix-ui/rect@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb"
+ integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==
+
"@react-aria/focus@^3.20.2":
version "3.21.3"
resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.21.3.tgz#bdcdfc45735c785de5ba54cc907e531926dcc4c5"
@@ -1195,6 +1491,20 @@ arg@^5.0.0:
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+aria-hidden@^1.2.4:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.6.tgz#73051c9b088114c795b1ea414e9c0fff874ffc1a"
+ integrity sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==
+ dependencies:
+ tslib "^2.0.0"
+
array-iterate@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-2.0.1.tgz#6efd43f8295b3fee06251d3d62ead4bd9805dd24"
@@ -1283,6 +1593,13 @@ chevrotain@~11.0.3:
"@chevrotain/utils" "11.0.3"
lodash-es "4.17.21"
+class-variance-authority@^0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
+ integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==
+ dependencies:
+ clsx "^2.1.1"
+
client-only@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
@@ -1297,7 +1614,7 @@ clipboardy@^4.0.0:
is-wsl "^3.1.0"
is64bit "^2.0.0"
-clsx@^2.0.0, clsx@^2.1.0:
+clsx@^2.0.0, clsx@^2.1.0, clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
@@ -1668,7 +1985,7 @@ dagre-d3-es@7.0.13:
d3 "^7.9.0"
lodash-es "^4.17.21"
-dayjs@^1.11.18:
+dayjs@^1.11.18, dayjs@^1.11.19:
version "1.11.19"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938"
integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==
@@ -1704,6 +2021,11 @@ detect-libc@^2.0.3, detect-libc@^2.1.2:
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
+detect-node-es@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
+ integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
+
devlop@^1.0.0, devlop@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018"
@@ -1761,6 +2083,11 @@ esm@^3.2.25:
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
estree-util-attach-comments@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz#344bde6a64c8a31d15231e5ee9e297566a691c2d"
@@ -1842,6 +2169,13 @@ execa@^8.0.1:
signal-exit "^4.1.0"
strip-final-newline "^3.0.0"
+extend-shallow@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==
+ dependencies:
+ is-extendable "^0.1.0"
+
extend@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
@@ -1889,6 +2223,11 @@ format@^0.2.0:
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==
+get-nonce@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
+ integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
+
get-stream@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
@@ -1911,6 +2250,16 @@ graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+gray-matter@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798"
+ integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==
+ dependencies:
+ js-yaml "^3.13.1"
+ kind-of "^6.0.2"
+ section-matter "^1.0.0"
+ strip-bom-string "^1.0.0"
+
hachure-fill@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/hachure-fill/-/hachure-fill-0.5.2.tgz#d19bc4cc8750a5962b47fb1300557a85fcf934cc"
@@ -2157,6 +2506,11 @@ is-docker@^3.0.0:
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200"
integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==
+is-extendable@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
+
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -2220,6 +2574,14 @@ jiti@^2.6.1:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92"
integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==
+js-yaml@^3.13.1:
+ version "3.14.2"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0"
+ integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
katex@^0.16.0, katex@^0.16.21, katex@^0.16.22:
version "0.16.27"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.27.tgz#4ecf6f620e0ca1c1a5de722e85fcdcec49086a48"
@@ -2232,6 +2594,11 @@ khroma@^2.1.0:
resolved "https://registry.yarnpkg.com/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1"
integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==
+kind-of@^6.0.0, kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
langium@3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/langium/-/langium-3.3.1.tgz#da745a40d5ad8ee565090fed52eaee643be4e591"
@@ -2342,6 +2709,11 @@ longest-streak@^3.0.0:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
+lucide-react@^0.563.0:
+ version "0.563.0"
+ resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.563.0.tgz#9a660d6f009942914a0df42391cf7d7d4dbcc713"
+ integrity sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==
+
magic-string@^0.30.21:
version "0.30.21"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91"
@@ -3357,6 +3729,33 @@ react-medium-image-zoom@^5.2.12:
resolved "https://registry.yarnpkg.com/react-medium-image-zoom/-/react-medium-image-zoom-5.4.0.tgz#b89c74a4f631289e8a7a21af26614c58fff0ea81"
integrity sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg==
+react-remove-scroll-bar@^2.3.7:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223"
+ integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==
+ dependencies:
+ react-style-singleton "^2.2.2"
+ tslib "^2.0.0"
+
+react-remove-scroll@^2.6.3:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz#6442da56791117661978ae99cd29be9026fecca0"
+ integrity sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==
+ dependencies:
+ react-remove-scroll-bar "^2.3.7"
+ react-style-singleton "^2.2.3"
+ tslib "^2.1.0"
+ use-callback-ref "^1.3.3"
+ use-sidecar "^1.1.3"
+
+react-style-singleton@^2.2.2, react-style-singleton@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388"
+ integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==
+ dependencies:
+ get-nonce "^1.0.0"
+ tslib "^2.0.0"
+
react@19.1.0:
version "19.1.0"
resolved "https://registry.yarnpkg.com/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75"
@@ -3568,6 +3967,11 @@ remark-stringify@^11.0.0:
mdast-util-to-markdown "^2.0.0"
unified "^11.0.0"
+reselect@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e"
+ integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==
+
retext-latin@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/retext-latin/-/retext-latin-4.0.0.tgz#d02498aa1fd39f1bf00e2ff59b1384c05d0c7ce3"
@@ -3654,6 +4058,14 @@ scroll-into-view-if-needed@^3.1.0:
dependencies:
compute-scroll-into-view "^3.0.2"
+section-matter@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167"
+ integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==
+ dependencies:
+ extend-shallow "^2.0.1"
+ kind-of "^6.0.0"
+
semver@^7.7.3:
version "7.7.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
@@ -3758,6 +4170,11 @@ speech-rule-engine@^4.0.6:
commander "13.1.0"
wicked-good-xpath "1.3.0"
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
stringify-entities@^4.0.0:
version "4.0.4"
resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3"
@@ -3766,6 +4183,11 @@ stringify-entities@^4.0.0:
character-entities-html4 "^2.0.0"
character-entities-legacy "^3.0.0"
+strip-bom-string@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92"
+ integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==
+
strip-final-newline@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
@@ -3802,11 +4224,16 @@ system-architecture@^0.1.0:
resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d"
integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==
-tabbable@^6.0.0:
+tabbable@^6.0.0, tabbable@^6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.4.0.tgz#36eb7a06d80b3924a22095daf45740dea3bf5581"
integrity sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==
+tailwind-merge@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-3.4.0.tgz#5a264e131a096879965f1175d11f8c36e6b64eca"
+ integrity sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==
+
tailwindcss@4.1.18, tailwindcss@^4.1.18:
version "4.1.18"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.18.tgz#f488ba47853abdb5354daf9679d3e7791fc4f4e3"
@@ -3869,11 +4296,16 @@ ts-morph@^27.0.0:
"@ts-morph/common" "~0.28.1"
code-block-writer "^13.0.3"
-tslib@^2.4.0, tslib@^2.8.0:
+tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.8.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+tw-animate-css@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/tw-animate-css/-/tw-animate-css-1.4.0.tgz#b4a06f68244cba39428aa47e65e6e4c0babc21ee"
+ integrity sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==
+
twoslash-protocol@0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/twoslash-protocol/-/twoslash-protocol-0.3.6.tgz#841b7c6217ddec8a475a4e088b64cb9f80ef0fe3"
@@ -4024,7 +4456,22 @@ unist-util-visit@^5.0.0:
unist-util-is "^6.0.0"
unist-util-visit-parents "^6.0.0"
-use-sync-external-store@^1.5.0:
+use-callback-ref@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf"
+ integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==
+ dependencies:
+ tslib "^2.0.0"
+
+use-sidecar@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb"
+ integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==
+ dependencies:
+ detect-node-es "^1.1.0"
+ tslib "^2.0.0"
+
+use-sync-external-store@^1.5.0, use-sync-external-store@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d"
integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==