Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 177 additions & 33 deletions .claude/rules/sim-settings-pages.md

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions .claude/rules/sim-styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ paths:

## Tailwind

1. **No inline styles** - Use Tailwind classes
1. **No inline styles** - Use Tailwind classes. Exception: a genuinely dynamic
value (a hashed avatar colour, a brand tile background) that cannot be a class.
2. **No duplicate dark classes** - Skip `dark:` when value matches light mode
3. **Exact values** - `text-[14px]`, `h-[26px]`
3. **Exact values over approximations** - `h-[26px]`, not `h-6`. But **type size is
always a named token** (`text-sm`, `text-caption`) — never `text-[14px]`, which
sets font-size only and inherits a different line-height. See
`sim-settings-pages.md` for the scale.
4. **Transitions** - `transition-colors` for interactive states

## Conditional Classes
Expand Down
34 changes: 32 additions & 2 deletions .claude/skills/add-settings-page/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ skill is the procedure.

Key paths:
- Layout primitive: `apps/sim/app/workspace/[workspaceId]/settings/components/settings-panel/settings-panel.tsx`
- Nav metadata (titles + descriptions): `apps/sim/app/workspace/[workspaceId]/settings/navigation.ts`
- Nav metadata (titles + descriptions): `apps/sim/components/settings/navigation.ts`
- Section switch + provider: `apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx`
- Pages: `apps/sim/app/workspace/[workspaceId]/settings/components/<name>/<name>.tsx` and EE pages under `apps/sim/ee/<feature>/components/`

Expand Down Expand Up @@ -53,7 +53,9 @@ For each page component, confirm the checklist in `.claude/rules/sim-settings-pa
3. Find literal pixel text sizes (should be 0 — see "Text-scale tokens" in
`.claude/rules/sim-settings-pages.md` for the token map and the row
title/subtitle pairing convention):
`git grep -n "text-\[1[0-8]px\]" -- 'apps/sim/**/settings/' 'apps/sim/ee/'`
`git grep -nE "text-\[1[0-8]px\]" -- 'apps/sim/**/settings/' 'apps/sim/ee/'` — should
be 0. Display type above the scale (`text-[40px]` hero headings, the `text-[8px]`
member-avatar initial) is deliberate and out of scope.
4. Confirm each page imports `SettingsPanel` and that its `NavigationItem` has an
accurate `description` of consistent length with its peers.
- Editable pages: confirm Save/Discard go through `SaveDiscardActions` and
Expand All @@ -77,3 +79,31 @@ For each page component, confirm the checklist in `.claude/rules/sim-settings-pa
8. **Verify the whole sweep:** `tsc --noEmit`, `biome check` on every touched
file, and run the affected pages' tests. Diff each file against the base and
confirm the change is purely structural before shipping.

## Mode C — Migrate list rows to `SettingsResourceRow`

Read "The resource row" in `.claude/rules/sim-settings-pages.md` first — it is the
contract. Then, per page:

1. Find hand-rolled rows:
`git grep -n "truncate text-\[var(--text-body)\] text-sm" -- 'apps/sim/app/workspace/' 'apps/sim/ee/'`
Every match outside `settings-resource-row.tsx` is either a row to migrate or a
genuinely different shape (multi-line body, tabular columns, a grid) that stays
bespoke — decide which, and say so.
2. Replace the row *and* its wrapper: a `<button>`/`<Link>` around the row becomes
`onClick`/`href` on the row itself. Wrapping the row is what the primitive
exists to stop — it is also invalid HTML once `trailing` holds a control.
3. Sort the trailing content: interactive → `trailing`, decorative → `badge`.
Getting this backwards makes the row's right edge a dead zone.
4. Add `navigable` only if the row opens a detail page, and `clickLabel` always.
5. Drop the container's `-mx-2` — the row now owns the bleed. Use
`RESOURCE_LIST_STACK` / `RESOURCE_LIST_GRID`; do not hand-write the gap.
6. Unlike Mode B, this migration **may** change conditional rendering: a
`<button disabled={!can}>` becomes `onClick={can ? … : undefined}` +
`navigable={can}`, which renders a plain non-interactive row. Verify the gated
state has no clickable affordance left.
7. Check what the old row rendered *beside* the title (a badge, a timestamp, a
transport label). The row's title truncates as one unit, so anything folded
into it can be ellipsised away — move it to `description` or `badge`.
8. Verify: `tsc --noEmit`, `biome check`, the page's tests, and a diff read of
every converted block for lost props, conditions, and `key` placement.
18 changes: 16 additions & 2 deletions apps/docs/content/docs/en/platform/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ This is an observability projection only. Secret resolution and workflow behavio
Masking is activated only when Sim successfully resolves a value from **Settings → Secrets** through `{{KEY}}`. A hardcoded literal, direct `environmentVariables['KEY']` read, or shell `$KEY` read does not activate it by itself. Once activated, every exact occurrence of that value in the run's log-facing content is masked. Encoded, hashed, or otherwise transformed versions are not matched. Do not deliberately return or print secrets.
</Callout>

### Copilot code execution

Copilot's Function and code-execution tools receive a saved secret only when their code explicitly contains a valid `{{KEY}}` reference. Direct `environmentVariables.KEY` access, shell `$KEY`, dynamic names, literals, and configured-but-unused secrets do not mount a value. Code execution requires workspace write access, and the caller must also be allowed to view the raw value: your own Personal secrets, any secret for which you are a Credential Admin, and Workspace secrets when you are a workspace admin. Credential Members can continue using shared secrets through normal workflow and tool resolution, but cannot mount their plaintext into arbitrary Copilot code.

Headless surfaces use their saved **Secret access** setting:

- **Sim Chat block** — under **Show additional fields**
- **Scheduled Tasks** — in the task modal
- **Inbox** — under **Settings → Inbox → Secrets**

Choose **All secrets** or **Selected secrets**. Existing configurations default to **All secrets** for compatibility. **All secrets** still means only secrets explicitly referenced with `{{KEY}}` that the execution actor may view; it never injects the full environment. Inbox messages from allowed external senders do not receive raw-secret access.

Code receives the real authorized value at runtime. Before any Copilot-visible tool result is returned, exact occurrences of activated secret values are replaced with `{{KEY}}`; local side effects and runtime results are not rewritten. Encoded, hashed, URL-encoded, otherwise transformed, or network-exfiltrated values cannot be inferred and masked reliably, so code should not deliberately return, transform, print, or transmit secrets to unintended destinations.

## Secret Details

Click **Details** on any secret row to open its detail view.
Expand Down Expand Up @@ -122,8 +136,8 @@ When a workflow runs, secrets resolve in this order:

<FAQ items={[
{ question: "Are my secrets encrypted at rest?", answer: "Yes. Values saved under Secrets are encrypted before being stored in the database." },
{ question: "Can a saved secret still appear in a workflow result?", answer: "Yes. Functional data is not rewritten, so the raw value can still reach downstream blocks, tools, and models and can appear in workflow execution responses, streams, or callbacks if your workflow deliberately returns or prints it. Log-facing views and read APIs, including Workflow Output on the Logs Overview and the Logs block's Get Run Details output, receive a protected copy after a successful {{KEY}} substitution." },
{ question: "What happens if both a workspace secret and a personal secret have the same key name?", answer: "The workspace secret takes precedence. During execution, the resolver checks workspace secrets first and uses personal secrets only as a fallback. This ensures production workflows use the shared, team-managed value." },
{ question: "Can a saved secret still appear in a workflow result?", answer: "Yes. Functional workflow data is not rewritten, so the raw value can still reach downstream blocks, tools, and models and can appear in workflow execution responses, streams, or callbacks if your workflow deliberately returns or prints it. Log-facing views and read APIs receive a protected copy after a successful {{KEY}} substitution. Copilot-visible tool results also mask exact activated values, but transformed values remain outside that protection." },
{ question: "What happens if both a workspace secret and a personal secret have the same key name?", answer: "Among secrets available to the execution actor, the workspace secret takes precedence and the personal secret is the fallback. An inaccessible workspace secret does not shadow an authorized personal value." },
{ question: "Who determines which personal secret is used for automated runs?", answer: "For manual runs, the personal secrets of the user who clicked Run are used as fallback. For automated runs triggered by API, webhook, or schedule, the personal secrets of the workflow owner are used instead." },
{ question: "Can I import secrets from a .env file?", answer: "Yes. Paste .env-style content (KEY=VALUE format) into any key or value field and the secrets will be auto-populated. The parser supports export KEY=VALUE, quoted values, and inline comments." },
{ question: "What happens if I delete a secret that is used in a workflow?", answer: "The workflow will fail at any block that references the deleted secret during execution because the value cannot be resolved. Update any references before deleting a secret." },
Expand Down
Loading
Loading