Skip to content

Commit 62d3140

Browse files
authored
v0.7.24: frontend perf improvements, md editor fixes, mailer fix, tools audit
2 parents 616a71d + cad44b9 commit 62d3140

448 files changed

Lines changed: 36088 additions & 7327 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.

.agents/skills/react-query-best-practices/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Read these before analyzing:
3131

3232
### Query hooks
3333
- Every `queryFn` must forward `signal` for request cancellation
34-
- Every query must have an explicit `staleTime` (default 0 is almost never correct)
34+
- Every query must have an explicit `staleTime` (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
3535
- `keepPreviousData` / `placeholderData` only on variable-key queries (where params change), never on static keys
3636
- Use `enabled` to prevent queries from running without required params
3737

.claude/commands/add-trigger.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export const {service}PollingHandler: PollingProviderHandler = {
374374

375375
try {
376376
// For OAuth services:
377-
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId, logger)
377+
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId)
378378
const config = webhookData.providerConfig as unknown as {Service}WebhookConfig
379379

380380
// First poll: seed state, emit nothing
@@ -421,7 +421,7 @@ export const {service}PollingTrigger: TriggerConfig = {
421421
polling: true, // REQUIRED — routes to polling infrastructure
422422

423423
subBlocks: [
424-
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger', supportsCredentialSets: true },
424+
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger' },
425425
// ... service-specific config fields (dropdowns, inputs, switches) ...
426426
{ id: 'triggerInstructions', type: 'text', title: 'Setup Instructions', hideFromPreview: true, mode: 'trigger', defaultValue: '...' },
427427
],

.claude/commands/react-query-best-practices.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Read these before analyzing:
3131

3232
### Query hooks
3333
- Every `queryFn` must forward `signal` for request cancellation
34-
- Every query must have an explicit `staleTime` (default 0 is almost never correct)
34+
- Every query must have an explicit `staleTime` (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
3535
- `keepPreviousData` / `placeholderData` only on variable-key queries (where params change), never on static keys
3636
- Use `enabled` to prevent queries from running without required params
3737

.claude/rules/sim-queries.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Next.js rewrites **every** export of a `'use client'` module into a *client refe
3434
So any **query-key factory, standalone `requestJson` fetcher, mapper, or constant** that a server module imports must live in a **non-`'use client'`** module:
3535

3636
- key factories → `hooks/queries/utils/<entity>-keys.ts` (see `folder-keys.ts`, `table-keys.ts`, `credential-keys.ts`)
37-
- standalone fetchers/mappers → `hooks/queries/utils/fetch-*.ts` / `*-list-query.ts` (see `fetch-workflow-envelope.ts`, `fetch-credential-set.ts`)
37+
- standalone fetchers/mappers → `hooks/queries/utils/fetch-*.ts` / `*-list-query.ts` (see `fetch-workflow-envelope.ts`, `fetch-workspace-credentials.ts`)
3838

3939
The `'use client'` hook module then imports these back for its hooks. **Never** define a server-imported factory/fetcher directly in a `'use client'` hooks file — it crashes SSR (this caused the tables-page crash). Enforced for prefetch/route/trigger/block files by `scripts/check-client-boundary-imports.ts` (`bun run check:client-boundary`, run in CI). Escape hatch for a genuinely browser-only path: `// client-boundary-allow: <reason>` on the line above the import.
4040

@@ -50,14 +50,16 @@ The `'use client'` hook module then imports these back for its hooks. **Never**
5050
## Query Hook
5151

5252
- Every `queryFn` must destructure and forward `signal` for request cancellation
53-
- Every query must have an explicit `staleTime`
53+
- Every query must have an explicit `staleTime`, assigned from a named exported constant (`ENTITY_LIST_STALE_TIME`), never an inline numeric literal. A server-side prefetch (`prefetch.ts`) hydrating the same query key must import and reuse that constant, not restate the number — this is what keeps a prefetched cache entry from going stale out of sync with the client hook that reads it
5454
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
5555
- Same-origin JSON calls must go through `requestJson(contract, ...)` from `@/lib/api/client/request` against the contract in `@/lib/api/contracts/**`
5656

5757
```typescript
5858
import { requestJson } from '@/lib/api/client/request'
5959
import { listEntitiesContract, type EntityList } from '@/lib/api/contracts/entities'
6060

61+
export const ENTITY_LIST_STALE_TIME = 60 * 1000
62+
6163
async function fetchEntities(workspaceId: string, signal?: AbortSignal): Promise<EntityList> {
6264
const data = await requestJson(listEntitiesContract, {
6365
query: { workspaceId },
@@ -71,7 +73,7 @@ export function useEntityList(workspaceId?: string, options?: { enabled?: boolea
7173
queryKey: entityKeys.list(workspaceId),
7274
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
7375
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
74-
staleTime: 60 * 1000,
76+
staleTime: ENTITY_LIST_STALE_TIME,
7577
placeholderData: keepPreviousData, // OK: workspaceId varies
7678
})
7779
}

.claude/rules/sim-settings-pages.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,55 @@ Adding a new settings page:
9696
`settings/[section]/settings.tsx`.
9797
3. Build the component body inside `<SettingsPanel>` — no shell, no title block.
9898

99+
## Text-scale tokens (no literal pixel sizes)
100+
101+
Settings pages never use a literal `text-[Npx]` class — always the named Tailwind
102+
scale token from `apps/sim/tailwind.config.ts`'s `fontSize` extension (`text-micro`
103+
10px, `text-xs` 11px, `text-caption` 12px, `text-small` 13px, `text-sm` 14px
104+
[Tailwind default, unmodified], `text-base` 15px, `text-md` 16px, `text-lg` 18px
105+
[Tailwind default]). A literal size is either a straight rename to the equivalent
106+
token (if the pixel value matches one exactly) or a sign the page never migrated —
107+
grep `text-\[1[0-8]px\]` under `apps/sim/app/workspace/*/settings/**` and
108+
`apps/sim/ee/**` to find stragglers.
109+
110+
For a two-line list row (title/value on top, a muted subtitle below — a name +
111+
email, a tool name + description, a server name + status), the established
112+
pairing is:
113+
114+
- **Title / row value**: `text-[var(--text-body)] text-sm`
115+
- **Subtitle / muted description**: `text-[var(--text-muted)] text-caption`
116+
117+
This is not a stylistic guess — it is the tokenized form of the literal-pixel
118+
pairing (`text-[14px] text-[var(--text-body)]` / `text-[12px]
119+
text-[var(--text-muted)]`) already used for this exact row shape across
120+
`member-list.tsx`, `api-keys.tsx`, `mcp.tsx`, `billing.tsx`,
121+
`workflow-mcp-servers.tsx`, and others — keep new rows consistent with it rather
122+
than inventing a new size pairing.
123+
124+
For a toggle row (a `Switch` with a title and optional description), use the emcn
125+
`Label` component for the title — never a hand-rolled `<span>` — paired with
126+
`Switch`'s `id`/`Label`'s `htmlFor`:
127+
128+
```tsx
129+
<div className='flex items-center justify-between'>
130+
<div className='flex flex-col gap-1'>
131+
<Label htmlFor='my-toggle'>Enable thing</Label>
132+
<p className='text-[var(--text-muted)] text-caption'>One-line description.</p>
133+
</div>
134+
<Switch id='my-toggle' checked={enabled} onCheckedChange={onToggle} />
135+
</div>
136+
```
137+
138+
`Label`'s own default styling (`font-medium text-[var(--text-primary)]
139+
text-small`) already matches the established title treatment — do not add a
140+
`className` overriding its size/color unless the row genuinely needs something
141+
different.
142+
143+
`--text-primary`/`--text-secondary` and `--text-body`/`--text-muted` are both real,
144+
independently-defined tokens (not interchangeable — they resolve to different
145+
colors) and both see legitimate use across settings pages; this rule only pins
146+
down the **row title/subtitle** shape above, not every text element on every page.
147+
99148
## Other shared settings primitives (do not re-roll these)
100149

101150
- **`SettingsEmptyState`** (`…/components/settings-empty-state`) — the canonical
@@ -154,7 +203,7 @@ changes" modal:
154203
## Detail sub-views
155204

156205
A drill-down view reached from a list row (selected MCP server, workflow MCP
157-
server, credential set, permission group, retention policy) renders through
206+
server, permission group, retention policy) renders through
158207
`SettingsPanel` like a list page: pass `back={{ text, icon: ArrowLeft, onSelect }}`
159208
for the left back chip, `title` (the entity name), and the header `actions`, then
160209
render the body. Do NOT hand-roll a shell or header bar; a tab bar renders as the
@@ -175,4 +224,5 @@ A settings page is design-system-clean when:
175224
- [ ] Detail sub-views and entitlement/loading gates keep their own chrome (intentional).
176225
- [ ] If it has editable state: Save/Discard go through `SaveDiscardActions`, dirty is wired via `useSettingsUnsavedGuard` (called before any early-return gate), and there is **no** hand-rolled Save button / `beforeunload` / "Unsaved changes" modal.
177226
- [ ] No business logic, handlers, or conditional rendering changed by the migration.
227+
- [ ] No literal `text-[Npx]` classes — named scale tokens only (see "Text-scale tokens" above).
178228
- [ ] `tsc`, `biome`, and the page's tests pass.

.claude/skills/add-settings-page/SKILL.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,30 @@ For each page component, confirm the checklist in `.claude/rules/sim-settings-pa
5050
**gate** early-return. Anything else is a page that still needs migrating.
5151
2. Find hand-rolled title blocks (should be 0 outside detail views):
5252
`git grep -n "text-\[var(--text-body)\] text-lg" -- 'apps/sim/**/settings/' 'apps/sim/ee/'`
53-
3. Confirm each page imports `SettingsPanel` and that its `NavigationItem` has an
53+
3. Find literal pixel text sizes (should be 0 — see "Text-scale tokens" in
54+
`.claude/rules/sim-settings-pages.md` for the token map and the row
55+
title/subtitle pairing convention):
56+
`git grep -n "text-\[1[0-8]px\]" -- 'apps/sim/**/settings/' 'apps/sim/ee/'`
57+
4. Confirm each page imports `SettingsPanel` and that its `NavigationItem` has an
5458
accurate `description` of consistent length with its peers.
5559
- Editable pages: confirm Save/Discard go through `SaveDiscardActions` and
5660
dirty is wired via `useSettingsUnsavedGuard` (called before early-return
5761
gates) — flag any hand-rolled Save button, `beforeunload`, or unsaved modal.
5862
`git grep -n "beforeunload" -- 'apps/sim/**/settings/' 'apps/sim/ee/'`
5963
should only hit the centralized `use-settings-before-unload.ts`.
60-
4. When migrating a page, change ONLY the structural shell→`SettingsPanel` swap:
64+
5. When migrating a page, change ONLY the structural shell→`SettingsPanel` swap:
6165
move header chips to `actions`, the standalone search to `search`, delete the
6266
`<h1>` title block, replace the three closing `</div>` (column/scroll/shell)
6367
with `</SettingsPanel>`, and keep modal siblings in a `<>` fragment. Do NOT
6468
touch handlers, state, queries, conditional rendering, or detail/gate returns.
6569
Drop per-page `gap-*`/`pt-*` on the content column in favor of the panel default.
66-
5. Remove now-unused imports (`ChipInput`/`Search`) ONLY after grepping that
70+
6. When fixing literal pixel text sizes, replace ONLY the size class with its
71+
exact-pixel-equivalent named token (e.g. `text-[12px]``text-caption`,
72+
never a different size) — this must render pixel-identical, not restyle the
73+
page. Leave color tokens (`--text-primary` vs `--text-body`, etc.) untouched
74+
unless they're also being changed for an unrelated, deliberate reason.
75+
7. Remove now-unused imports (`ChipInput`/`Search`) ONLY after grepping that
6776
they are not still used elsewhere in the file (e.g. by a detail view).
68-
6. **Verify the whole sweep:** `tsc --noEmit`, `biome check` on every touched
77+
8. **Verify the whole sweep:** `tsc --noEmit`, `biome check` on every touched
6978
file, and run the affected pages' tests. Diff each file against the base and
7079
confirm the change is purely structural before shipping.

.cursor/commands/add-trigger.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ export const {service}PollingHandler: PollingProviderHandler = {
369369

370370
try {
371371
// For OAuth services:
372-
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId, logger)
372+
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId)
373373
const config = webhookData.providerConfig as unknown as {Service}WebhookConfig
374374

375375
// First poll: seed state, emit nothing
@@ -416,7 +416,7 @@ export const {service}PollingTrigger: TriggerConfig = {
416416
polling: true, // REQUIRED — routes to polling infrastructure
417417

418418
subBlocks: [
419-
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger', supportsCredentialSets: true },
419+
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger' },
420420
// ... service-specific config fields (dropdowns, inputs, switches) ...
421421
{ id: 'triggerInstructions', type: 'text', title: 'Setup Instructions', hideFromPreview: true, mode: 'trigger', defaultValue: '...' },
422422
],

.cursor/commands/react-query-best-practices.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Read these before analyzing:
2626

2727
### Query hooks
2828
- Every `queryFn` must forward `signal` for request cancellation
29-
- Every query must have an explicit `staleTime` (default 0 is almost never correct)
29+
- Every query must have an explicit `staleTime` (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
3030
- `keepPreviousData` / `placeholderData` only on variable-key queries (where params change), never on static keys
3131
- Use `enabled` to prevent queries from running without required params
3232

CLAUDE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query'
290290
import { requestJson } from '@/lib/api/client/request'
291291
import { listEntitiesContract, type EntityList } from '@/lib/api/contracts/entities'
292292

293+
export const ENTITY_LIST_STALE_TIME = 60 * 1000
294+
293295
async function fetchEntities(workspaceId: string, signal?: AbortSignal): Promise<EntityList> {
294296
const data = await requestJson(listEntitiesContract, {
295297
query: { workspaceId },
@@ -303,7 +305,7 @@ export function useEntityList(workspaceId?: string) {
303305
queryKey: entityKeys.list(workspaceId),
304306
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
305307
enabled: Boolean(workspaceId),
306-
staleTime: 60 * 1000,
308+
staleTime: ENTITY_LIST_STALE_TIME,
307309
placeholderData: keepPreviousData,
308310
})
309311
}
@@ -326,7 +328,7 @@ export const entityKeys = {
326328
### Query Hooks
327329

328330
- Every `queryFn` must forward `signal` for request cancellation
329-
- Every query must have an explicit `staleTime`
331+
- Every query must have an explicit `staleTime`, assigned from a named exported constant, never an inline numeric literal — a server-side prefetch hydrating the same query key must import and reuse that constant so the two never drift out of sync
330332
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
331333

332334
```typescript
@@ -335,7 +337,7 @@ export function useEntityList(workspaceId?: string) {
335337
queryKey: entityKeys.list(workspaceId),
336338
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
337339
enabled: Boolean(workspaceId),
338-
staleTime: 60 * 1000,
340+
staleTime: ENTITY_LIST_STALE_TIME,
339341
placeholderData: keepPreviousData, // OK: workspaceId varies
340342
})
341343
}

apps/docs/content/docs/de/enterprise/index.mdx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ Für selbst gehostete Bereitstellungen können Enterprise-Funktionen über Umgeb
7979
| Variable | Beschreibung |
8080
|----------|-------------|
8181
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Single Sign-On mit SAML/OIDC |
82-
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling-Gruppen für E-Mail-Trigger |
8382
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Workspace-/Organisations-Einladungen global deaktivieren |
8483

8584
<Callout type="warn">

0 commit comments

Comments
 (0)