chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,193 @@
'use client'
import { useId, useMemo, useState } from 'react'
import { Checkbox, ChevronDown, cn } from '@sim/emcn'
import {
ForkFileTree,
type ForkFlatFile,
groupForkFilesIntoFolders,
} from '@/ee/workspace-forking/components/fork-file-tree/fork-file-tree'
/** A flat copyable resource (table / KB / custom tool / skill / MCP server) in the picker. */
export interface ForkResourcePickerItem {
id: string
label: string
}
interface ResourceKindRowProps {
label: string
items: ForkResourcePickerItem[]
selected: Set<string>
/** Toggle the given ids on/off. Used by the select-all header checkbox. */
onToggleMany: (ids: string[], checked: boolean) => void
onToggleItem: (id: string, checked: boolean) => void
disabled?: boolean
}
/**
* One expandable resource kind in the fork / sync copy picker: a tri-state "select all" header
* (count of selected / total) plus, when expanded, a scrollable list of individual resources so
* the user can copy a specific subset. Shared by the fork modal's "Copy resources" and the sync
* modal's "Copy resources" so the two surfaces stay identical. Files nest in a folder tree
* instead - use {@link FileKindRow}.
*
* Expanded body uses the settings MCP expand pattern (`border-t` + `--surface-2`) so items
* read as contained in the kind rather than indented siblings of other kinds.
*/
export function ResourceKindRow({
label,
items,
selected,
onToggleMany,
onToggleItem,
disabled = false,
}: ResourceKindRowProps) {
const [expanded, setExpanded] = useState(false)
const fieldId = useId()
const total = items.length
const selectedCount = items.reduce((count, item) => count + (selected.has(item.id) ? 1 : 0), 0)
const headerState = selectedCount === 0 ? false : selectedCount === total ? true : 'indeterminate'
return (
<div className='flex flex-col'>
<div className='flex items-center gap-2 text-[var(--text-body)] text-sm'>
<Checkbox
size='sm'
aria-label={`Copy all ${label}`}
checked={headerState}
onCheckedChange={() =>
onToggleMany(
items.map((item) => item.id),
headerState !== true
)
}
disabled={disabled}
/>
<button
type='button'
className='flex min-w-0 flex-1 items-center gap-1 text-left hover:text-[var(--text-primary)]'
onClick={() => setExpanded((value) => !value)}
>
<span className='min-w-0 flex-1 truncate'>
{label} ({selectedCount > 0 ? `${selectedCount}/${total}` : total})
</span>
<ChevronDown
className={cn(
'h-[6px] w-[10px] flex-shrink-0 text-[var(--text-icon)] transition-transform',
expanded && 'rotate-180'
)}
/>
</button>
</div>
{expanded ? (
<div className='mt-1 max-h-44 overflow-y-auto border-[var(--border-1)] border-t bg-[var(--surface-2)] px-2.5 py-2'>
<div className='flex flex-col gap-0.5'>
{items.map((item) => {
const isChecked = selected.has(item.id)
const itemId = `${fieldId}-${item.id}`
return (
<label
key={item.id}
htmlFor={itemId}
className={cn(
'flex min-w-0 items-center gap-2 rounded-md py-0.5 text-[var(--text-body)] text-sm',
disabled
? 'cursor-not-allowed opacity-60'
: 'cursor-pointer hover:text-[var(--text-primary)]'
)}
>
<Checkbox
id={itemId}
size='sm'
checked={isChecked}
onCheckedChange={(checked) => onToggleItem(item.id, checked === true)}
disabled={disabled}
/>
<span className='truncate'>{item.label}</span>
</label>
)
})}
</div>
</div>
) : null}
</div>
)
}
interface FileKindRowProps {
label: string
files: ForkFlatFile[]
selected: Set<string>
onToggleAll: (selectAll: boolean) => void
onToggleItem: (id: string, checked: boolean) => void
onToggleMany: (ids: string[], checked: boolean) => void
disabled?: boolean
}
/**
* The Files kind: a tri-state "select all" header (count selected / total) that expands to a
* folder ▸ file tree, so the user can copy a whole folder or a specific file. Files are the only
* copyable kind that nests; every other kind uses the flat {@link ResourceKindRow}. Shared by the
* fork and sync copy pickers so both group files identically.
*/
export function FileKindRow({
label,
files,
selected,
onToggleAll,
onToggleItem,
onToggleMany,
disabled = false,
}: FileKindRowProps) {
const [expanded, setExpanded] = useState(false)
const total = files.length
const selectedCount = files.filter((file) => selected.has(file.id)).length
const headerState = selectedCount === 0 ? false : selectedCount === total ? true : 'indeterminate'
const { folders, rootFiles } = useMemo(() => groupForkFilesIntoFolders(files), [files])
return (
<div className='flex flex-col'>
<div className='flex items-center gap-2 text-[var(--text-body)] text-sm'>
<Checkbox
size='sm'
aria-label={`Copy all ${label}`}
checked={headerState}
onCheckedChange={() => onToggleAll(headerState !== true)}
disabled={disabled}
/>
<button
type='button'
className='flex min-w-0 flex-1 items-center gap-1 text-left hover:text-[var(--text-primary)]'
onClick={() => setExpanded((value) => !value)}
>
<span className='min-w-0 flex-1 truncate'>
{label} ({selectedCount > 0 ? `${selectedCount}/${total}` : total})
</span>
<ChevronDown
className={cn(
'h-[6px] w-[10px] flex-shrink-0 text-[var(--text-icon)] transition-transform',
expanded && 'rotate-180'
)}
/>
</button>
</div>
{expanded ? (
<div className='mt-1 border-[var(--border-1)] border-t bg-[var(--surface-2)] px-2.5 py-2'>
<ForkFileTree
folders={folders}
rootFiles={rootFiles}
isSelected={(id) => selected.has(id)}
onToggleFile={onToggleItem}
onToggleMany={onToggleMany}
disabled={disabled}
/>
</div>
) : null}
</div>
)
}