From 4b1f2207b815858837985f85cd665c9e75e46ca7 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Mon, 6 Jul 2026 10:08:25 +0200 Subject: [PATCH] ci: replace expiring-PAT org membership gate with author_association (#9957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci for broken links + fix broken links * ci: replace expiring-PAT org membership gate with author_association The shared check-org-membership.yml reusable workflow authenticated to the GitHub API with the ORG_ACCESS_TOKEN PAT to confirm org membership. That PAT expired ~1 year after issuance, so the API could no longer see private org members and check-membership emitted is_member=false — silently skipping every auto-review, command-triggered review, /ai, /plan, and git-command job while still reporting success. Gate on the event payload's author_association (OWNER/MEMBER/COLLABORATOR) instead, which comes from the built-in GITHUB_TOKEN and never expires. The trusted internal bot and existing draft/fork/command guards are preserved; the workflow_call paths stay open as trusted upstream. Deletes the now-unused reusable workflow. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .github/scripts/check-docs-links.mjs | 126 ++++++++++++++++++ .github/workflows/check-docs-links.yml | 23 ++++ .github/workflows/check-org-membership.yml | 83 ------------ .github/workflows/claude-plan.yml | 22 ++- .github/workflows/claude.yml | 22 ++- .github/workflows/codex-pr-review.yml | 20 +-- .github/workflows/git-commands.yaml | 36 +++-- .github/workflows/pi-pr-review.yml | 20 +-- .github/workflows/pr-ready-review.yml | 19 +-- .github/workflows/pr-review-commands.yml | 25 ++-- .../apps/editor/component/components.ts | 14 +- 11 files changed, 213 insertions(+), 197 deletions(-) create mode 100644 .github/scripts/check-docs-links.mjs create mode 100644 .github/workflows/check-docs-links.yml delete mode 100644 .github/workflows/check-org-membership.yml diff --git a/.github/scripts/check-docs-links.mjs b/.github/scripts/check-docs-links.mjs new file mode 100644 index 0000000000..122d098197 --- /dev/null +++ b/.github/scripts/check-docs-links.mjs @@ -0,0 +1,126 @@ +// Extracts every windmill.dev/docs link referenced in the frontend source and +// verifies none of them 404. Run: `node .github/scripts/check-docs-links.mjs`. +// Used by the check-docs-links GitHub workflow (release / manual trigger only). + +import { readdir, readFile } from 'node:fs/promises' +import { join, extname } from 'node:path' + +const ROOT = 'frontend/src' +const EXTS = new Set(['.ts', '.js', '.svelte', '.mjs', '.cjs']) +const DOCS_RE = /https?:\/\/(?:www\.)?windmill\.dev\/docs\/[^\s"'`)>\]}]*/g +// `const someBaseUrl = 'https://www.windmill.dev/docs/...'` used later as `${someBaseUrl}/foo` +const BASE_RE = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*['"`](https?:\/\/(?:www\.)?windmill\.dev\/docs\/[^'"`]+)['"`]/g + +const CONCURRENCY = 24 +const TIMEOUT_MS = 20000 +const RETRIES = 2 + +async function walk(dir) { + const out = [] + for (const entry of await readdir(dir, { withFileTypes: true })) { + const p = join(dir, entry.name) + if (entry.isDirectory()) { + if (entry.name === 'node_modules' || entry.name === '.svelte-kit') continue + out.push(...(await walk(p))) + } else if (EXTS.has(extname(entry.name))) { + out.push(p) + } + } + return out +} + +// url (no fragment) -> Set of source files it appears in +const urls = new Map() +const unresolved = [] + +function record(url, file) { + const clean = url + .replace(/\\.*$/, '') // cut at an escape sequence embedded in a string literal (e.g. \n) + .replace(/#.*$/, '') // drop anchor fragment — irrelevant to a 404 check + .replace(/[.,;:'")\]]+$/, '') + if (!clean) return + // A `{`/`${` means the URL is built from an unresolved template/interpolation var. + if (clean.includes('{')) { + unresolved.push(`${clean} (${file})`) + return + } + if (!urls.has(clean)) urls.set(clean, new Set()) + urls.get(clean).add(file) +} + +for (const file of await walk(ROOT)) { + let content = await readFile(file, 'utf8') + // Inline file-local base-url constants so `${base}/page` template literals resolve. + const bases = [] + for (const m of content.matchAll(BASE_RE)) bases.push({ name: m[1], value: m[2], decl: m[0] }) + for (const { name, value } of bases) { + content = content.replaceAll('${' + name + '}', value) + } + // Blank each base declaration so a prefix-only base (no index page of its own, + // e.g. .../app_configuration_settings) isn't checked as a standalone link. + // A genuinely bare `${base}` usage was already inlined above, so it's still covered. + for (const { decl } of bases) content = content.replace(decl, '') + for (const m of content.matchAll(DOCS_RE)) record(m[0], file) +} + +const allUrls = [...urls.keys()].sort() +console.log(`Found ${allUrls.length} distinct docs links across ${ROOT}`) +if (unresolved.length) { + console.log(`\n⚠️ ${unresolved.length} link(s) built from an unrecognized base URL — skipped (register the base const so they can be checked):`) + for (const u of [...new Set(unresolved)].sort()) console.log(` ${u}`) +} + +async function check(url) { + for (let attempt = 0; attempt <= RETRIES; attempt++) { + const ctrl = new AbortController() + const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS) + try { + let res = await fetch(url, { + method: 'HEAD', + redirect: 'follow', + signal: ctrl.signal, + headers: { 'user-agent': 'windmill-docs-link-check' } + }) + // Some hosts reject HEAD — fall back to GET. + if (res.status === 405 || res.status === 501) { + res = await fetch(url, { + method: 'GET', + redirect: 'follow', + signal: ctrl.signal, + headers: { 'user-agent': 'windmill-docs-link-check' } + }) + } + clearTimeout(timer) + return { url, status: res.status, ok: res.status < 400 } + } catch (err) { + clearTimeout(timer) + if (attempt === RETRIES) return { url, status: 0, ok: false, error: String(err?.message || err) } + await new Promise((r) => setTimeout(r, 500 * (attempt + 1))) + } + } +} + +// Simple concurrency pool. +const results = [] +let idx = 0 +async function worker() { + while (idx < allUrls.length) { + const url = allUrls[idx++] + results.push(await check(url)) + } +} +await Promise.all(Array.from({ length: CONCURRENCY }, worker)) + +const failures = results.filter((r) => !r.ok) +if (failures.length === 0) { + console.log(`\n✅ All ${allUrls.length} docs links are reachable.`) + process.exit(0) +} + +console.log(`\n❌ ${failures.length} broken docs link(s):`) +for (const f of failures.sort((a, b) => a.url.localeCompare(b.url))) { + console.log(`\n ${f.url}`) + console.log(` status: ${f.error ? `error (${f.error})` : f.status}`) + for (const file of urls.get(f.url)) console.log(` ↳ ${file}`) +} +process.exit(1) diff --git a/.github/workflows/check-docs-links.yml b/.github/workflows/check-docs-links.yml new file mode 100644 index 0000000000..ec17c06771 --- /dev/null +++ b/.github/workflows/check-docs-links.yml @@ -0,0 +1,23 @@ +name: Check frontend docs links + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + check-docs-links: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + frontend/src + .github/scripts + - uses: actions/setup-node@v4 + with: + node-version: "22.x" + - name: Verify docs links are not 404 + run: node .github/scripts/check-docs-links.mjs diff --git a/.github/workflows/check-org-membership.yml b/.github/workflows/check-org-membership.yml deleted file mode 100644 index eb338d3188..0000000000 --- a/.github/workflows/check-org-membership.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Check Organization Membership - -on: - workflow_call: - inputs: - commenter: - required: false - type: string - default: '' - description: 'The username to check. Auto-detected from the event context if not provided.' - organization: - required: false - type: string - default: 'windmill-labs' - description: 'The organization to check membership for' - trusted_bot: - required: false - type: string - default: 'windmill-internal-app[bot]' - description: 'The trusted bot username to allow' - secrets: - access_token: - required: true - description: 'The access token to use for org membership check' - outputs: - is_member: - description: 'Whether the user is an organization member or trusted bot' - value: ${{ jobs.check-membership.outputs.is_member }} - -jobs: - check-membership: - runs-on: ubicloud-standard-2 - outputs: - is_member: ${{ steps.check-membership.outputs.is_member }} - steps: - - name: Determine commenter - id: determine-commenter - run: | - COMMENTER="${{ inputs.commenter }}" - if [[ -z "$COMMENTER" ]]; then - if [[ "${{ github.event_name }}" == "issue_comment" || \ - "${{ github.event_name }}" == "pull_request_review_comment" ]]; then - COMMENTER="${{ github.event.comment.user.login }}" - elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then - COMMENTER="${{ github.event.review.user.login }}" - else - COMMENTER="${{ github.event.issue.user.login }}" - fi - fi - echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT - - - name: Check organization membership - id: check-membership - env: - ORG_ACCESS_TOKEN: ${{ secrets.access_token }} - COMMENTER: ${{ steps.determine-commenter.outputs.commenter }} - ORG: ${{ inputs.organization }} - TRUSTED_BOT: ${{ inputs.trusted_bot }} - run: | - # 1. Allow the trusted bot straight away - if [[ "$COMMENTER" == "$TRUSTED_BOT" ]]; then - echo "is_member=true" >> $GITHUB_OUTPUT - exit 0 - fi - - # 2. Disallow other bots - if [[ "${COMMENTER}" =~ \[bot\]$ ]]; then - echo "is_member=false" >> $GITHUB_OUTPUT - exit 0 - fi - - # 3. Otherwise check if the user is a member of the organization - STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ - -H "Authorization: token $ORG_ACCESS_TOKEN" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "https://api.github.com/orgs/$ORG/members/$COMMENTER") - - if [ "$STATUS" -eq 204 ]; then - echo "is_member=true" >> $GITHUB_OUTPUT - else - echo "is_member=false" >> $GITHUB_OUTPUT - fi \ No newline at end of file diff --git a/.github/workflows/claude-plan.yml b/.github/workflows/claude-plan.yml index c63e3fe1aa..e4394fe7a1 100644 --- a/.github/workflows/claude-plan.yml +++ b/.github/workflows/claude-plan.yml @@ -11,20 +11,18 @@ on: types: [submitted] jobs: - check-membership: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/plan')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/plan')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '/plan')) || - (github.event_name == 'issues' && contains(github.event.issue.body, '/plan')) - uses: ./.github/workflows/check-org-membership.yml - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - claude-plan-action: - needs: check-membership if: | - needs.check-membership.outputs.is_member == 'true' + ( + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/plan')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/plan')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '/plan')) || + (github.event_name == 'issues' && contains(github.event.issue.body, '/plan')) + ) && + ( + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association) || + (github.event.comment.user.login || github.event.review.user.login || github.event.issue.user.login) == 'windmill-internal-app[bot]' + ) runs-on: ubicloud-standard-4 timeout-minutes: 20 permissions: diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index f5db652084..0a66553362 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -11,20 +11,18 @@ on: types: [submitted] jobs: - check-membership: - if: | - (github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) || - (github.event_name == 'pull_request_review_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) || - (github.event_name == 'pull_request_review' && startsWith(github.event.review.body, '/ai') && !startsWith(github.event.review.body, '/ai-fast')) || - (github.event_name == 'issues' && startsWith(github.event.issue.body, '/ai') && !startsWith(github.event.issue.body, '/ai-fast')) - uses: ./.github/workflows/check-org-membership.yml - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - claude-code-action: - needs: check-membership if: | - needs.check-membership.outputs.is_member == 'true' + ( + (github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) || + (github.event_name == 'pull_request_review_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) || + (github.event_name == 'pull_request_review' && startsWith(github.event.review.body, '/ai') && !startsWith(github.event.review.body, '/ai-fast')) || + (github.event_name == 'issues' && startsWith(github.event.issue.body, '/ai') && !startsWith(github.event.issue.body, '/ai-fast')) + ) && + ( + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association) || + (github.event.comment.user.login || github.event.review.user.login || github.event.issue.user.login) == 'windmill-internal-app[bot]' + ) runs-on: ubicloud-standard-8 permissions: contents: write diff --git a/.github/workflows/codex-pr-review.yml b/.github/workflows/codex-pr-review.yml index 66cc4b5f10..772e3e31ff 100644 --- a/.github/workflows/codex-pr-review.yml +++ b/.github/workflows/codex-pr-review.yml @@ -32,27 +32,15 @@ concurrency: cancel-in-progress: true jobs: - check-membership: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/check-org-membership.yml - with: - commenter: ${{ github.event.pull_request.user.login }} - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - codex-review: - needs: check-membership runs-on: ubicloud-standard-2 timeout-minutes: 30 if: | - always() && + github.event_name == 'workflow_call' || ( - needs.check-membership.result == 'skipped' || - (needs.check-membership.result == 'success' && needs.check-membership.outputs.is_member == 'true') - ) && - ( - github.event_name == 'workflow_call' || - (github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false) + github.event.pull_request.draft == false && + github.event.pull_request.head.repo.fork == false && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) ) permissions: contents: read diff --git a/.github/workflows/git-commands.yaml b/.github/workflows/git-commands.yaml index f1cc380563..68b6da3da8 100644 --- a/.github/workflows/git-commands.yaml +++ b/.github/workflows/git-commands.yaml @@ -5,21 +5,11 @@ on: types: [created] jobs: - check-membership: - if: >- - github.event.issue.pull_request && ( - startsWith(github.event.comment.body, '/updatesqlx') || - startsWith(github.event.comment.body, '/demo') || - startsWith(github.event.comment.body, '/eeref') || - startsWith(github.event.comment.body, '/docs') - ) - uses: ./.github/workflows/check-org-membership.yml - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - update-sqlx: - needs: check-membership - if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/updatesqlx') + if: >- + github.event.issue.pull_request && + (contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) || github.event.comment.user.login == 'windmill-internal-app[bot]') && + startsWith(github.event.comment.body, '/updatesqlx') runs-on: ubicloud-standard-8 permissions: contents: write @@ -147,8 +137,10 @@ jobs: }) demo: - needs: check-membership - if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/demo') + if: >- + github.event.issue.pull_request && + (contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) || github.event.comment.user.login == 'windmill-internal-app[bot]') && + startsWith(github.event.comment.body, '/demo') runs-on: ubicloud-standard-2 permissions: contents: read @@ -227,8 +219,10 @@ jobs: fi update-ee-ref: - needs: check-membership - if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/eeref') + if: >- + github.event.issue.pull_request && + (contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) || github.event.comment.user.login == 'windmill-internal-app[bot]') && + startsWith(github.event.comment.body, '/eeref') runs-on: ubicloud-standard-2 permissions: contents: write @@ -313,8 +307,10 @@ jobs: }) update-docs: - needs: check-membership - if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/docs') + if: >- + github.event.issue.pull_request && + (contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) || github.event.comment.user.login == 'windmill-internal-app[bot]') && + startsWith(github.event.comment.body, '/docs') runs-on: ubicloud-standard-2 permissions: contents: read diff --git a/.github/workflows/pi-pr-review.yml b/.github/workflows/pi-pr-review.yml index 9fbe43e9f0..4416e5674b 100644 --- a/.github/workflows/pi-pr-review.yml +++ b/.github/workflows/pi-pr-review.yml @@ -30,27 +30,15 @@ concurrency: cancel-in-progress: true jobs: - check-membership: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/check-org-membership.yml - with: - commenter: ${{ github.event.pull_request.user.login }} - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - pi-review: - needs: check-membership runs-on: ubicloud-standard-2 timeout-minutes: 30 if: | - always() && + github.event_name == 'workflow_call' || ( - needs.check-membership.result == 'skipped' || - (needs.check-membership.result == 'success' && needs.check-membership.outputs.is_member == 'true') - ) && - ( - github.event_name == 'workflow_call' || - (github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false) + github.event.pull_request.draft == false && + github.event.pull_request.head.repo.fork == false && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) ) permissions: contents: read diff --git a/.github/workflows/pr-ready-review.yml b/.github/workflows/pr-ready-review.yml index 4d722df4af..263696ceaf 100644 --- a/.github/workflows/pr-ready-review.yml +++ b/.github/workflows/pr-ready-review.yml @@ -30,26 +30,13 @@ concurrency: cancel-in-progress: true jobs: - check-membership: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/check-org-membership.yml - with: - commenter: ${{ github.event.pull_request.user.login }} - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - auto-review: - needs: check-membership runs-on: ubuntu-latest if: | - always() && + github.event_name == 'workflow_call' || ( - needs.check-membership.result == 'skipped' || - (needs.check-membership.result == 'success' && needs.check-membership.outputs.is_member == 'true') - ) && - ( - github.event_name == 'workflow_call' || - (github.event.pull_request.draft == false || github.event.pull_request.ready_for_review == true) + (github.event.pull_request.draft == false || github.event.pull_request.ready_for_review == true) && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) ) permissions: contents: read diff --git a/.github/workflows/pr-review-commands.yml b/.github/workflows/pr-review-commands.yml index ba55bfea2f..4678e2986c 100644 --- a/.github/workflows/pr-review-commands.yml +++ b/.github/workflows/pr-review-commands.yml @@ -42,16 +42,11 @@ jobs: ;; esac - check-membership: - needs: parse - if: needs.parse.outputs.command != '' - uses: ./.github/workflows/check-org-membership.yml - secrets: - access_token: ${{ secrets.ORG_ACCESS_TOKEN }} - acknowledge: - needs: [parse, check-membership] - if: needs.parse.outputs.command != '' && needs.check-membership.outputs.is_member == 'true' + needs: [parse] + if: | + needs.parse.outputs.command != '' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) runs-on: ubuntu-latest permissions: issues: write @@ -68,9 +63,9 @@ jobs: -f content=eyes >/dev/null claude: - needs: [parse, check-membership] + needs: [parse] if: | - needs.check-membership.outputs.is_member == 'true' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) && (needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'claude') permissions: contents: read @@ -86,9 +81,9 @@ jobs: WINDMILL_EE_PRIVATE_ACCESS: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} codex: - needs: [parse, check-membership] + needs: [parse] if: | - needs.check-membership.outputs.is_member == 'true' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) && (needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'codex') permissions: contents: read @@ -105,9 +100,9 @@ jobs: WINDMILL_EE_PRIVATE_ACCESS: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} pi: - needs: [parse, check-membership] + needs: [parse] if: | - needs.check-membership.outputs.is_member == 'true' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) && (needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'pi') permissions: contents: read diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts index 707d992799..eb4028752d 100644 --- a/frontend/src/lib/components/apps/editor/component/components.ts +++ b/frontend/src/lib/components/apps/editor/component/components.ts @@ -1171,7 +1171,7 @@ export const components = { chatcomponent: { name: 'Chat', icon: MessageSquare, - documentationLink: `${documentationBaseUrl}/chat`, + documentationLink: `${documentationBaseUrl}/app_component_library`, dims: '3:8-6:12' as AppComponentDimensions, customCss: { container: { class: '', style: '' }, @@ -1299,7 +1299,7 @@ export const components = { jobprogressbarcomponent: { name: 'Progress Bar by Job Id', icon: Monitor, - documentationLink: `${documentationBaseUrl}/progress_bar`, + documentationLink: `${documentationBaseUrl}/app_component_library`, dims: '2:2-6:2' as AppComponentDimensions, customCss: { header: { class: '', style: '' }, @@ -1467,7 +1467,7 @@ export const components = { name: 'Code Input', icon: Code, dims: '2:1-4:4' as AppComponentDimensions, - documentationLink: `${documentationBaseUrl}/code`, + documentationLink: `${documentationBaseUrl}/code_input`, customCss: { text: { class: '', style: '' }, container: { class: '', style: '' } @@ -1810,7 +1810,7 @@ export const components = { piechartcomponent: { name: 'Pie Chart', icon: PieChart, - documentationLink: `${documentationBaseUrl}/pie_chart`, + documentationLink: `${documentationBaseUrl}/chartjs`, dims: '2:8-6:8' as AppComponentDimensions, customCss: { container: { class: '', style: '' } @@ -1919,7 +1919,7 @@ export const components = { barchartcomponent: { name: 'Bar/Line Chart', icon: BarChart4, - documentationLink: `${documentationBaseUrl}/bar_line_chart`, + documentationLink: `${documentationBaseUrl}/chartjs`, dims: '2:8-6:8' as AppComponentDimensions, customCss: { container: { class: '', style: '' } @@ -2132,7 +2132,7 @@ This is a paragraph. timeseriescomponent: { name: 'Timeseries', icon: GripHorizontal, - documentationLink: `${documentationBaseUrl}/timeseries`, + documentationLink: `${documentationBaseUrl}/chartjs`, dims: '2:8-6:8' as AppComponentDimensions, customCss: { container: { class: '', style: '' } @@ -2206,7 +2206,7 @@ This is a paragraph. scatterchartcomponent: { name: 'Scatter Chart', icon: GripHorizontal, - documentationLink: `${documentationBaseUrl}/scatter_chart`, + documentationLink: `${documentationBaseUrl}/chartjs`, dims: '2:8-6:8' as AppComponentDimensions, customCss: { container: { class: '', style: '' }