Skip to content

Commit d7ef91a

Browse files
authored
v0.8.38: concurrent knowledgebase operations, media lightbox for chat
2 parents 6781650 + e21130b commit d7ef91a

130 files changed

Lines changed: 4430 additions & 959 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/global.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const filtered = filterUndefined(obj)
7373
```
7474

7575
## Deployment flags in the browser
76-
Client code inside a workspace reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never the `isHosted`/`isBillingEnabled` constants from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a tab recovered from one would render Sim Cloud as self-hosted. The reader is seeded from the server-resolved workspace host context. Server code keeps reading `env-flags`.
76+
Client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never the `isHosted`/`isBillingEnabled` constants from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a tab recovered from one would render Sim Cloud as self-hosted. The reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout. Server code keeps reading `env-flags`.
7777

7878
## Package Manager
7979
Use `bun` and `bunx`, not `npm` and `npx`.

.cursor/rules/global.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const filtered = filterUndefined(obj)
7676
```
7777

7878
## Deployment flags in the browser
79-
Client code inside a workspace reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never the `isHosted`/`isBillingEnabled` constants from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a tab recovered from one would render Sim Cloud as self-hosted. The reader is seeded from the server-resolved workspace host context. Server code keeps reading `env-flags`.
79+
Client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never the `isHosted`/`isBillingEnabled` constants from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a tab recovered from one would render Sim Cloud as self-hosted. The reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout. Server code keeps reading `env-flags`.
8080

8181
## Package Manager
8282
Use `bun` and `bunx`, not `npm` and `npx`.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ You are a professional software engineer. All code must follow best practices: a
1818
- `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — object trimming; never `Object.fromEntries(Object.entries(...).filter(...))`
1919
- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis
2020
- `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline
21-
- **Deployment flags in the browser**: client code inside a workspace reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. The constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit; the reader is seeded from the server-resolved workspace host context instead. Server code keeps reading `env-flags`
21+
- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. The constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`
2222
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
2323
- **Type-checking**: Run `bun run type-check` (per workspace) or `bunx turbo run type-check` (all of them). Do not remove the `@typescript/native` alias from the root `devDependencies` — nothing imports it, but it is what makes a bare `tsc` resolve to the native TypeScript 7 compiler instead of the ~10x slower JavaScript TypeScript 6 one that `@typescript/typescript6` pulls in transitively. `bun run check:native-typecheck` enforces this
2424

apps/docs/app/global.css

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,8 +1013,6 @@ div.flex.flex-row.items-start.bg-fd-secondary.border.rounded-lg.text-xs {
10131013
display: none !important;
10141014
}
10151015

1016-
/* Method+path bar — cleaner, lighter styling like Gumloop.
1017-
Override bg-fd-card CSS variable directly for reliability. */
10181016
#nd-page:has(.api-page-header) div.flex.flex-row.items-center.rounded-xl.border.not-prose {
10191017
--color-fd-card: var(--surface-3) !important;
10201018
background-color: var(--surface-3) !important;
Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

33
import { useRef, useState } from 'react'
4+
import { Lightbox } from '@sim/emcn'
45
import { cn, getAssetUrl } from '@/lib/utils'
5-
import { Lightbox } from './lightbox'
66

77
interface ActionImageProps {
88
src: string
@@ -17,10 +17,6 @@ interface ActionVideoProps {
1717
}
1818

1919
export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProps) {
20-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
21-
22-
const openLightbox = () => setIsLightboxOpen(true)
23-
2420
const image = (
2521
<img
2622
src={src}
@@ -32,42 +28,28 @@ export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProp
3228
/>
3329
)
3430

31+
if (!enableLightbox) return image
32+
3533
return (
36-
<>
37-
{enableLightbox ? (
38-
<button
39-
type='button'
40-
onClick={openLightbox}
41-
aria-label={`Open ${alt} in media viewer`}
42-
className='group inline-block cursor-pointer rounded p-0 text-left'
43-
>
44-
{image}
45-
</button>
46-
) : (
47-
image
48-
)}
49-
{enableLightbox && (
50-
<Lightbox
51-
isOpen={isLightboxOpen}
52-
onClose={() => setIsLightboxOpen(false)}
53-
src={src}
54-
alt={alt}
55-
type='image'
56-
/>
57-
)}
58-
</>
34+
<Lightbox src={src} alt={alt}>
35+
<button
36+
type='button'
37+
aria-label={`Open ${alt} in media viewer`}
38+
className='group inline-block cursor-pointer rounded p-0 text-left'
39+
>
40+
{image}
41+
</button>
42+
</Lightbox>
5943
)
6044
}
6145

6246
export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProps) {
6347
const videoRef = useRef<HTMLVideoElement>(null)
64-
const startTimeRef = useRef(0)
65-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
48+
const [startTime, setStartTime] = useState(0)
6649
const resolvedSrc = getAssetUrl(src)
6750

6851
const openLightbox = () => {
69-
startTimeRef.current = videoRef.current?.currentTime ?? 0
70-
setIsLightboxOpen(true)
52+
setStartTime(videoRef.current?.currentTime ?? 0)
7153
}
7254

7355
const video = (
@@ -85,30 +67,18 @@ export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProp
8567
/>
8668
)
8769

70+
if (!enableLightbox) return video
71+
8872
return (
89-
<>
90-
{enableLightbox ? (
91-
<button
92-
type='button'
93-
onClick={openLightbox}
94-
aria-label={`Open ${alt} in media viewer`}
95-
className='group inline-block cursor-pointer rounded p-0 text-left'
96-
>
97-
{video}
98-
</button>
99-
) : (
100-
video
101-
)}
102-
{enableLightbox && (
103-
<Lightbox
104-
isOpen={isLightboxOpen}
105-
onClose={() => setIsLightboxOpen(false)}
106-
src={src}
107-
alt={alt}
108-
type='video'
109-
startTime={startTimeRef.current}
110-
/>
111-
)}
112-
</>
73+
<Lightbox src={resolvedSrc} alt={alt} type='video' startTime={startTime}>
74+
<button
75+
type='button'
76+
onClick={openLightbox}
77+
aria-label={`Open ${alt} in media viewer`}
78+
className='group inline-block cursor-pointer rounded p-0 text-left'
79+
>
80+
{video}
81+
</button>
82+
</Lightbox>
11383
)
11484
}

apps/docs/components/ui/image.tsx

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client'
22

3-
import { useState } from 'react'
3+
import { Lightbox } from '@sim/emcn'
44
import NextImage, { type ImageProps as NextImageProps } from 'next/image'
5-
import { Lightbox } from '@/components/ui/lightbox'
65
import { cn } from '@/lib/utils'
76

87
interface ImageProps extends Omit<NextImageProps, 'className'> {
@@ -17,9 +16,7 @@ export function Image({
1716
src,
1817
...props
1918
}: ImageProps) {
20-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
21-
22-
const openLightbox = () => setIsLightboxOpen(true)
19+
const lightboxSrc = typeof src === 'string' ? src : 'default' in src ? src.default.src : src.src
2320

2421
const image = (
2522
<NextImage
@@ -34,30 +31,17 @@ export function Image({
3431
/>
3532
)
3633

37-
return (
38-
<>
39-
{enableLightbox ? (
40-
<button
41-
type='button'
42-
onClick={openLightbox}
43-
aria-label={`Open ${alt} in media viewer`}
44-
className='group contents'
45-
>
46-
{image}
47-
</button>
48-
) : (
49-
image
50-
)}
34+
if (!enableLightbox) return image
5135

52-
{enableLightbox && (
53-
<Lightbox
54-
isOpen={isLightboxOpen}
55-
onClose={() => setIsLightboxOpen(false)}
56-
src={typeof src === 'string' ? src : String(src)}
57-
alt={alt}
58-
type='image'
59-
/>
60-
)}
61-
</>
36+
return (
37+
<Lightbox src={lightboxSrc} alt={alt}>
38+
<button
39+
type='button'
40+
aria-label={`Open ${alt || 'image'} in media viewer`}
41+
className='group contents'
42+
>
43+
{image}
44+
</button>
45+
</Lightbox>
6246
)
6347
}

apps/docs/components/ui/lightbox.tsx

Lines changed: 0 additions & 104 deletions
This file was deleted.

apps/docs/components/ui/video.tsx

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

33
import { useEffect, useRef, useState } from 'react'
4+
import { Lightbox } from '@sim/emcn'
45
import { cn, getAssetUrl } from '@/lib/utils'
5-
import { Lightbox } from './lightbox'
66

77
interface VideoProps {
88
src: string
@@ -28,8 +28,7 @@ export function Video({
2828
height,
2929
}: VideoProps) {
3030
const videoRef = useRef<HTMLVideoElement>(null)
31-
const startTimeRef = useRef(0)
32-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
31+
const [startTime, setStartTime] = useState(0)
3332
const [isInView, setIsInView] = useState(false)
3433

3534
useEffect(() => {
@@ -55,8 +54,7 @@ export function Video({
5554
}, [])
5655

5756
const openLightbox = () => {
58-
startTimeRef.current = videoRef.current?.currentTime ?? 0
59-
setIsLightboxOpen(true)
57+
setStartTime(videoRef.current?.currentTime ?? 0)
6058
}
6159

6260
const video = (
@@ -78,31 +76,18 @@ export function Video({
7876
/>
7977
)
8078

81-
return (
82-
<>
83-
{enableLightbox ? (
84-
<button
85-
type='button'
86-
onClick={openLightbox}
87-
aria-label={`Open ${src} in media viewer`}
88-
className='group contents'
89-
>
90-
{video}
91-
</button>
92-
) : (
93-
video
94-
)}
79+
if (!enableLightbox) return video
9580

96-
{enableLightbox && (
97-
<Lightbox
98-
isOpen={isLightboxOpen}
99-
onClose={() => setIsLightboxOpen(false)}
100-
src={src}
101-
alt={`Video: ${src}`}
102-
type='video'
103-
startTime={startTimeRef.current}
104-
/>
105-
)}
106-
</>
81+
return (
82+
<Lightbox src={getAssetUrl(src)} alt={`Video: ${src}`} type='video' startTime={startTime}>
83+
<button
84+
type='button'
85+
onClick={openLightbox}
86+
aria-label={`Open ${src} in media viewer`}
87+
className='group contents'
88+
>
89+
{video}
90+
</button>
91+
</Lightbox>
10792
)
10893
}

0 commit comments

Comments
 (0)