diff --git a/src/app/[lang]/contact/page.tsx b/src/app/[lang]/contact/page.tsx index e7d584e6..b58d5fb7 100644 --- a/src/app/[lang]/contact/page.tsx +++ b/src/app/[lang]/contact/page.tsx @@ -21,8 +21,8 @@ export default function ContactPage() { const links = [ { icon: Github, title: "GitHub", desc: p.contact_github_desc, href: GITHUB_REPOSITORY_URL, external: true }, { icon: MessageSquare, title: p.contact_issues_title, desc: p.contact_issues_desc, href: GITHUB_ISSUES_URL, external: true }, - { icon: Map, title: t.common.request_tool, desc: "Open a sanitized request for a local-first tool or workflow.", href: GITHUB_FEATURE_REQUEST_URL, external: true }, - { icon: ThumbsUp, title: t.common.vote_on_requests, desc: "Use GitHub reactions to signal demand without sharing payloads.", href: GITHUB_REQUEST_VOTING_URL, external: true }, + { icon: Map, title: t.common.request_tool, desc: p.contact_request_tool_desc, href: GITHUB_FEATURE_REQUEST_URL, external: true }, + { icon: ThumbsUp, title: t.common.vote_on_requests, desc: p.contact_vote_requests_desc, href: GITHUB_REQUEST_VOTING_URL, external: true }, { icon: ShieldCheck, title: p.contact_security_title, desc: p.contact_security_desc, href: SECURITY_ADVISORY_URL, external: true }, ] @@ -74,28 +74,28 @@ export default function ContactPage() {
-

Public planning

+

{p.contact_public_planning_title}

- Use public requests only with sanitized examples. Do not post real secrets, private payloads, HAR files, private URLs, logs, prompts, generated output, request bodies, or response bodies. + {p.contact_public_planning_desc}

- Roadmap + {p.contact_roadmap_link} - Extension and desktop research + {p.contact_distribution_research_link} - Self-hosting + {p.contact_self_hosting_link}
diff --git a/src/app/[lang]/install-app/page.tsx b/src/app/[lang]/install-app/page.tsx index 8494b31f..1e1ef794 100644 --- a/src/app/[lang]/install-app/page.tsx +++ b/src/app/[lang]/install-app/page.tsx @@ -15,6 +15,7 @@ export default async function InstallAppPage({ params }: { params: Promise<{ lan const allToolsLabel = t.common.all_tools const trustCenterLabel = t.pages.trust_center_title const localDataControlsLabel = t.common.local_data_controls.title + const distributionResearchLabel = t.pages.contact_distribution_research_link const offlineMatrixTitle = t.pages.trust_center_offline_matrix_title const offlineMatrixDescription = t.pages.trust_center_offline_matrix_install_desc const offlineMatrixLink = t.pages.trust_center_offline_matrix_link @@ -26,6 +27,7 @@ export default async function InstallAppPage({ params }: { params: Promise<{ lan allToolsLabel={allToolsLabel} trustCenterLabel={trustCenterLabel} localDataControlsLabel={localDataControlsLabel} + distributionResearchLabel={distributionResearchLabel} offlineMatrixTitle={offlineMatrixTitle} offlineMatrixDescription={offlineMatrixDescription} offlineMatrixLink={offlineMatrixLink} diff --git a/src/core/i18n/translations/de.json b/src/core/i18n/translations/de.json index 8b77d27a..7d32d65f 100644 --- a/src/core/i18n/translations/de.json +++ b/src/core/i18n/translations/de.json @@ -3297,6 +3297,13 @@ "pricing_internal_deployment_title": "Interne Bereitstellung", "pricing_internal_deployment_desc": "Self-Hosting bewahrt das Versprechen ohne Cloud-Verlauf: keine gehosteten Konten, keine Payload-Synchronisierung und keine serverseitige Verarbeitung von Tool-Payloads.", "pricing_internal_deployment_cta": "Interne Bereitstellung anfragen", - "pricing_self_hosting_guide": "Self-Hosting-Anleitung" + "pricing_self_hosting_guide": "Self-Hosting-Anleitung", + "contact_request_tool_desc": "Erstellen Sie eine bereinigte Anfrage fuer ein local-first Tool oder einen Workflow.", + "contact_vote_requests_desc": "Nutzen Sie GitHub-Reaktionen, um Bedarf zu zeigen, ohne Payloads zu teilen.", + "contact_public_planning_title": "Oeffentliche Planung", + "contact_public_planning_desc": "Nutzen Sie oeffentliche Anfragen nur mit bereinigten Beispielen. Posten Sie keine echten Secrets, privaten Payloads, HAR-Dateien, privaten URLs, Logs, Prompts, generierten Ausgaben, Request-Bodies oder Response-Bodies.", + "contact_roadmap_link": "Fahrplan", + "contact_distribution_research_link": "Erweiterungs- und Desktop-Recherche", + "contact_self_hosting_link": "Selbsthosting" } } diff --git a/src/core/i18n/translations/en.json b/src/core/i18n/translations/en.json index 41d8a965..5d7ab57a 100644 --- a/src/core/i18n/translations/en.json +++ b/src/core/i18n/translations/en.json @@ -3297,6 +3297,13 @@ "pricing_internal_deployment_title": "Internal deployment guidance", "pricing_internal_deployment_desc": "Self-hosting keeps the no cloud history promise: no hosted accounts, no payload sync, and no server-side tool payload processing.", "pricing_internal_deployment_cta": "Ask about internal deployment", - "pricing_self_hosting_guide": "Self-hosting guide" + "pricing_self_hosting_guide": "Self-hosting guide", + "contact_request_tool_desc": "Open a sanitized request for a local-first tool or workflow.", + "contact_vote_requests_desc": "Use GitHub reactions to signal demand without sharing payloads.", + "contact_public_planning_title": "Public planning", + "contact_public_planning_desc": "Use public requests only with sanitized examples. Do not post real secrets, private payloads, HAR files, private URLs, logs, prompts, generated output, request bodies, or response bodies.", + "contact_roadmap_link": "Roadmap", + "contact_distribution_research_link": "Extension and desktop research", + "contact_self_hosting_link": "Self-hosting" } } diff --git a/src/core/i18n/translations/fr.json b/src/core/i18n/translations/fr.json index 6b557094..73745603 100644 --- a/src/core/i18n/translations/fr.json +++ b/src/core/i18n/translations/fr.json @@ -3297,6 +3297,13 @@ "pricing_internal_deployment_title": "Conseil de déploiement interne", "pricing_internal_deployment_desc": "L’auto-hébergement conserve la promesse sans historique cloud : pas de comptes hébergés, pas de synchronisation de payload et aucun traitement serveur des payloads d’outil.", "pricing_internal_deployment_cta": "Demander un déploiement interne", - "pricing_self_hosting_guide": "Guide d’auto-hébergement" + "pricing_self_hosting_guide": "Guide d’auto-hébergement", + "contact_request_tool_desc": "Ouvrez une demande nettoyee pour un outil ou workflow local-first.", + "contact_vote_requests_desc": "Utilisez les reactions GitHub pour signaler la demande sans partager de payloads.", + "contact_public_planning_title": "Planification publique", + "contact_public_planning_desc": "Utilisez les demandes publiques uniquement avec des exemples nettoyes. Ne publiez pas de vrais secrets, payloads prives, fichiers HAR, URL privees, logs, prompts, sorties generees, corps de requete ou corps de reponse.", + "contact_roadmap_link": "Feuille de route", + "contact_distribution_research_link": "Recherche extension et desktop", + "contact_self_hosting_link": "Auto-hebergement" } } diff --git a/src/core/i18n/translations/ja.json b/src/core/i18n/translations/ja.json index f6c3de75..17d4911d 100644 --- a/src/core/i18n/translations/ja.json +++ b/src/core/i18n/translations/ja.json @@ -3194,10 +3194,17 @@ "contact_github_desc": "バグ報告、機能要望、コードへの貢献はこちら。", "contact_issues_title": "Issue とフィードバック", "contact_issues_desc": "バグ報告や機能要望は GitHub Issue でお知らせください。", + "contact_request_tool_desc": "ローカルファーストのツールやワークフローのリクエストを、サニタイズ済みの内容で送れます。", + "contact_vote_requests_desc": "ペイロードを共有せずに、GitHub のリアクションで需要を示せます。", "contact_email_title": "メール", "contact_email_desc": "ビジネスに関するお問い合わせや提携のご相談はこちら。", "contact_security_title": "セキュリティ脆弱性", "contact_security_desc": "疑わしい脆弱性は GitHub Security Advisories で報告してください。公開 issue に秘密情報や非公開 payload を含めないでください。", + "contact_public_planning_title": "公開計画", + "contact_public_planning_desc": "公開リクエストではサニタイズ済みの例だけを使ってください。実際のシークレット、非公開ペイロード、HAR ファイル、非公開 URL、ログ、プロンプト、生成出力、リクエスト本文、レスポンス本文は投稿しないでください。", + "contact_roadmap_link": "ロードマップ", + "contact_distribution_research_link": "拡張機能とデスクトップ調査", + "contact_self_hosting_link": "セルフホスティング", "privacy_external_request_tools_title": "外部リクエストツール", "privacy_external_request_tools_desc": "この一覧はツール manifest から自動生成されます。これらのツールは、表示された目的のために、外部リクエスト操作を選んだ後だけ記載ドメインへ接続します。", "trust_center_title": "プライバシーと信頼センター", diff --git a/src/core/i18n/translations/ko.json b/src/core/i18n/translations/ko.json index 7235bc40..443b20cc 100644 --- a/src/core/i18n/translations/ko.json +++ b/src/core/i18n/translations/ko.json @@ -3194,10 +3194,17 @@ "contact_github_desc": "버그 제보, 기능 요청 또는 코드 기여를 남겨 주세요.", "contact_issues_title": "이슈 및 피드백", "contact_issues_desc": "버그 신고나 기능 요청은 GitHub Issue로 등록해 주세요.", + "contact_request_tool_desc": "로컬 우선 도구나 워크플로 요청을 정리된 예시로 제출하세요.", + "contact_vote_requests_desc": "payload를 공유하지 않고 GitHub 반응으로 수요를 표시하세요.", "contact_email_title": "이메일", "contact_email_desc": "비즈니스 문의나 파트너십 제안은 이메일로 연락해 주세요.", "contact_security_title": "보안 취약점", "contact_security_desc": "의심되는 취약점은 GitHub Security Advisories로 신고하세요. 공개 issue에는 비밀값이나 비공개 payload를 포함하지 마세요.", + "contact_public_planning_title": "공개 계획", + "contact_public_planning_desc": "공개 요청에는 정리된 예시만 사용하세요. 실제 비밀값, 비공개 payload, HAR 파일, 비공개 URL, 로그, 프롬프트, 생성된 출력, 요청 본문, 응답 본문을 올리지 마세요.", + "contact_roadmap_link": "로드맵", + "contact_distribution_research_link": "확장 프로그램 및 데스크톱 조사", + "contact_self_hosting_link": "셀프 호스팅", "privacy_external_request_tools_title": "외부 요청 도구", "privacy_external_request_tools_desc": "이 목록은 도구 manifest에서 자동 생성됩니다. 이러한 도구는 공개된 목적에 한해 나열된 도메인에 접근하며, 외부 요청 작업을 선택한 후에만 실행됩니다.", "trust_center_title": "개인정보 및 신뢰 센터", diff --git a/src/core/i18n/translations/zh-CN.json b/src/core/i18n/translations/zh-CN.json index 59d24ca2..34d62ad0 100644 --- a/src/core/i18n/translations/zh-CN.json +++ b/src/core/i18n/translations/zh-CN.json @@ -3194,10 +3194,17 @@ "contact_github_desc": "报告 bug、提出功能需求,或参与代码贡献。", "contact_issues_title": "问题与反馈", "contact_issues_desc": "如需反馈 bug 或功能建议,请在 GitHub 提交 issue。", + "contact_request_tool_desc": "为本地优先的工具或工作流提交已脱敏的请求。", + "contact_vote_requests_desc": "用 GitHub reaction 表达需求,不需要分享 payload。", "contact_email_title": "邮箱", "contact_email_desc": "商务合作或伙伴关系咨询,请通过邮件联系。", "contact_security_title": "安全漏洞", "contact_security_desc": "请通过 GitHub Security Advisories 报告疑似漏洞。不要在公开 issue 中包含密钥或私有 payload。", + "contact_public_planning_title": "公开规划", + "contact_public_planning_desc": "公开请求只使用已脱敏示例。不要发布真实密钥、私有 payload、HAR 文件、私有 URL、日志、提示词、生成输出、请求正文或响应正文。", + "contact_roadmap_link": "路线图", + "contact_distribution_research_link": "扩展与桌面端调研", + "contact_self_hosting_link": "自托管", "privacy_external_request_tools_title": "外部请求工具", "privacy_external_request_tools_desc": "此清单由工具 manifest 自动生成。这些工具只会为声明用途访问列出的域名,并且只会在你选择外部请求操作后执行。", "trust_center_title": "隐私与信任中心", diff --git a/src/core/i18n/translations/zh-TW.json b/src/core/i18n/translations/zh-TW.json index 00610eaf..83c657bf 100644 --- a/src/core/i18n/translations/zh-TW.json +++ b/src/core/i18n/translations/zh-TW.json @@ -3194,10 +3194,17 @@ "contact_github_desc": "回報 bug、提出功能需求,或參與程式碼貢獻。", "contact_issues_title": "問題與回饋", "contact_issues_desc": "若要回報 bug 或提出功能建議,請在 GitHub 提交 issue。", + "contact_request_tool_desc": "為本地優先的工具或工作流程提交已去識別化的請求。", + "contact_vote_requests_desc": "用 GitHub reaction 表達需求,不需要分享 payload。", "contact_email_title": "電子郵件", "contact_email_desc": "商務合作或夥伴關係洽詢,請透過郵件聯繫。", "contact_security_title": "安全漏洞", "contact_security_desc": "請透過 GitHub Security Advisories 回報疑似漏洞。不要在公開 issue 中包含金鑰或私有 payload。", + "contact_public_planning_title": "公開規劃", + "contact_public_planning_desc": "公開請求只使用已去識別化範例。不要發布真實密鑰、私有 payload、HAR 檔案、私有 URL、日誌、提示詞、生成輸出、請求本文或回應本文。", + "contact_roadmap_link": "路線圖", + "contact_distribution_research_link": "擴充功能與桌面端調研", + "contact_self_hosting_link": "自架託管", "privacy_external_request_tools_title": "外部請求工具", "privacy_external_request_tools_desc": "此清單由工具 manifest 自動產生。這些工具只會為聲明用途存取列出的網域,且只會在你選擇外部請求操作後執行。", "trust_center_title": "隱私與信任中心", diff --git a/src/features/install-app/components/install-app-client.tsx b/src/features/install-app/components/install-app-client.tsx index 5e859762..7efa7804 100644 --- a/src/features/install-app/components/install-app-client.tsx +++ b/src/features/install-app/components/install-app-client.tsx @@ -36,6 +36,7 @@ type InstallAppClientProps = { allToolsLabel: string trustCenterLabel: string localDataControlsLabel: string + distributionResearchLabel: string offlineMatrixTitle: string offlineMatrixDescription: string offlineMatrixLink: string @@ -47,6 +48,7 @@ export function InstallAppClient({ allToolsLabel, trustCenterLabel, localDataControlsLabel, + distributionResearchLabel, offlineMatrixTitle, offlineMatrixDescription, offlineMatrixLink, @@ -322,7 +324,7 @@ export function InstallAppClient({ diff --git a/src/lib/sitemap-lastmod.json b/src/lib/sitemap-lastmod.json index eea9f370..c11eb81d 100644 --- a/src/lib/sitemap-lastmod.json +++ b/src/lib/sitemap-lastmod.json @@ -211,13 +211,13 @@ "fr": "2026-06-25T00:00:00.000Z" }, "contact": { - "en": "2026-06-25T00:00:00.000Z", - "zh-CN": "2026-06-25T00:00:00.000Z", - "zh-TW": "2026-06-25T00:00:00.000Z", - "ja": "2026-06-25T00:00:00.000Z", - "ko": "2026-06-25T00:00:00.000Z", - "de": "2026-06-25T00:00:00.000Z", - "fr": "2026-06-25T00:00:00.000Z" + "en": "2026-06-29T00:00:00.000Z", + "zh-CN": "2026-06-29T00:00:00.000Z", + "zh-TW": "2026-06-29T00:00:00.000Z", + "ja": "2026-06-29T00:00:00.000Z", + "ko": "2026-06-29T00:00:00.000Z", + "de": "2026-06-29T00:00:00.000Z", + "fr": "2026-06-29T00:00:00.000Z" }, "privacy": { "en": "2026-06-26T00:00:00.000Z", @@ -283,13 +283,13 @@ "fr": "2026-06-14T00:00:00.000Z" }, "install-app": { - "en": "2026-06-26T00:00:00.000Z", - "zh-CN": "2026-06-26T00:00:00.000Z", - "zh-TW": "2026-06-26T00:00:00.000Z", - "ja": "2026-06-26T00:00:00.000Z", - "ko": "2026-06-26T00:00:00.000Z", - "de": "2026-06-26T00:00:00.000Z", - "fr": "2026-06-26T00:00:00.000Z" + "en": "2026-06-29T00:00:00.000Z", + "zh-CN": "2026-06-29T00:00:00.000Z", + "zh-TW": "2026-06-29T00:00:00.000Z", + "ja": "2026-06-29T00:00:00.000Z", + "ko": "2026-06-29T00:00:00.000Z", + "de": "2026-06-29T00:00:00.000Z", + "fr": "2026-06-29T00:00:00.000Z" }, "workflows": { "en": "2026-06-22T00:00:00.000Z", diff --git a/tests/component/install-app-analytics.test.tsx b/tests/component/install-app-analytics.test.tsx index c686d680..ce43cfe1 100644 --- a/tests/component/install-app-analytics.test.tsx +++ b/tests/component/install-app-analytics.test.tsx @@ -60,6 +60,7 @@ describe("install app analytics", () => { allToolsLabel="All tools" trustCenterLabel="Trust Center" localDataControlsLabel="Local data controls" + distributionResearchLabel="Extension and desktop research" offlineMatrixTitle="Offline support matrix" offlineMatrixDescription="Review which workflows keep running after cache warm-up." offlineMatrixLink="Offline matrix" diff --git a/tests/component/install-app-page.test.tsx b/tests/component/install-app-page.test.tsx index 48a79513..ce7893f9 100644 --- a/tests/component/install-app-page.test.tsx +++ b/tests/component/install-app-page.test.tsx @@ -49,6 +49,7 @@ describe("install app page", () => { allToolsLabel="All tools" trustCenterLabel="Trust Center" localDataControlsLabel="Local data controls" + distributionResearchLabel="Extension and desktop research" offlineMatrixTitle="Offline support matrix" offlineMatrixDescription="Review which workflows keep running after cache warm-up." offlineMatrixLink="Offline matrix" @@ -75,6 +76,7 @@ describe("install app page", () => { allToolsLabel="所有工具" trustCenterLabel="信任中心" localDataControlsLabel="本地数据控制" + distributionResearchLabel="扩展与桌面端调研" offlineMatrixTitle="离线支持矩阵" offlineMatrixDescription="查看缓存预热后哪些流程可继续运行。" offlineMatrixLink="离线矩阵" @@ -85,6 +87,8 @@ describe("install app page", () => { expect(browseLink).toHaveAttribute("href", getAllToolsHref("zh-CN")) expect(browseLink).not.toHaveAttribute("href", "/zh-CN/format-validate") expect(screen.getByRole("link", { name: "信任中心" })).toHaveAttribute("href", "/zh-CN/trust-center") + expect(screen.getByRole("link", { name: "扩展与桌面端调研" })).toHaveAttribute("href", "/zh-CN/distribution-research") + expect(screen.queryByRole("link", { name: "Extension and desktop research" })).not.toBeInTheDocument() const previewImage = screen.getByTestId("mock-next-image") expect(previewImage).toHaveAttribute("aria-label", `${copy.guides.chrome_desktop.label} ${copy.guidePreviewLabel}`) @@ -111,6 +115,7 @@ describe("install app page", () => { allToolsLabel="All tools" trustCenterLabel="Trust Center" localDataControlsLabel="Local data controls" + distributionResearchLabel="Extension and desktop research" offlineMatrixTitle="Offline support matrix" offlineMatrixDescription="Review which workflows keep running after cache warm-up." offlineMatrixLink="Offline matrix" @@ -142,6 +147,7 @@ describe("install app page", () => { allToolsLabel="All tools" trustCenterLabel="Trust Center" localDataControlsLabel="Local data controls" + distributionResearchLabel="Extension and desktop research" offlineMatrixTitle="Offline support matrix" offlineMatrixDescription="Review which workflows keep running after cache warm-up." offlineMatrixLink="Offline matrix" diff --git a/tests/guards/legacy-audit-closure-guard.test.ts b/tests/guards/legacy-audit-closure-guard.test.ts index a9a2d058..720c7566 100644 --- a/tests/guards/legacy-audit-closure-guard.test.ts +++ b/tests/guards/legacy-audit-closure-guard.test.ts @@ -42,7 +42,8 @@ describe("legacy audit closure guard", () => { const smoke = read("scripts/e2e/run-playwright-smoke.js") expect(layoutComponents).toContain("exposes language and theme controls inside the mobile navigation sheet") - expect(publicPlanning).toContain("Extension and desktop research") + expect(publicPlanning).toContain("renders contact public-planning CTAs in localized route copy") + expect(publicPlanning).toContain("扩展与桌面端调研") expect(publicPlanning).toContain("Vote on launcher demand") expect(publicPlanning).toContain("issues/new?template=feature_request.yml") expect(pricing).toContain("self_hosting_title") @@ -52,6 +53,7 @@ describe("legacy audit closure guard", () => { expect(smoke).toContain("assertHeaderKeyboardPaths") expect(smoke).toContain("assertMobileNavigationKeyboardPath") expect(smoke).toContain("assertMobileAllTools") - expect(installApp).toContain("Extension and desktop research") + expect(installApp).toContain("distributionResearchLabel") + expect(installApp).toContain("zh-CN/distribution-research") }) }) diff --git a/tests/guards/localized-copy-guard.test.ts b/tests/guards/localized-copy-guard.test.ts index 742d1eae..54cf5494 100644 --- a/tests/guards/localized-copy-guard.test.ts +++ b/tests/guards/localized-copy-guard.test.ts @@ -40,6 +40,24 @@ const FILE_GUARDS: Array<{ file: string; forbidden: Array }> = "SHA-1 est considere comme faible pour la securite. Preferez SHA-256 ou un algorithme plus robuste pour les nouveaux systemes.", ], }, + { + file: "src/features/install-app/components/install-app-client.tsx", + forbidden: [ + "Extension and desktop research", + ], + }, + { + file: "src/app/[lang]/contact/page.tsx", + forbidden: [ + "Open a sanitized request for a local-first tool or workflow.", + "Use GitHub reactions to signal demand without sharing payloads.", + ">Public planning<", + "Use public requests only with sanitized examples.", + ">Roadmap<", + ">Extension and desktop research<", + ">Self-hosting<", + ], + }, { file: "src/lib/localized-meta-copy.ts", forbidden: [ diff --git a/tests/guards/public-planning-docs.test.ts b/tests/guards/public-planning-docs.test.ts index e13c9215..67b8ba0f 100644 --- a/tests/guards/public-planning-docs.test.ts +++ b/tests/guards/public-planning-docs.test.ts @@ -59,6 +59,23 @@ describe("public roadmap, changelog, self-hosting, and distribution docs", () => expect(markup).toContain("Do not post real secrets") }) + it("renders contact public-planning CTAs in localized route copy", () => { + const markup = renderToStaticMarkup(createElement( + LangProvider, + { lang: "zh-CN", translations: getTranslation("zh-CN") }, + createElement(ContactPage), + )) + + expect(markup).toContain("公开规划") + expect(markup).toContain("路线图") + expect(markup).toContain("扩展与桌面端调研") + expect(markup).toContain("自托管") + expect(markup).toContain("已脱敏") + expect(markup).not.toContain("Public planning") + expect(markup).not.toContain("Extension and desktop research") + expect(markup).not.toContain("Use public requests only with sanitized examples") + }) + it("keeps the All Tools empty state wired to privacy-safe request and voting flows", () => { const allToolsPage = fs.readFileSync(path.join(process.cwd(), "src/app/[lang]/all-tools/page.tsx"), "utf8") const allToolsDiscovery = fs.readFileSync(path.join(process.cwd(), "src/features/tool-discovery/all-tools-discovery.tsx"), "utf8")