From 86a1e3c02c82ce122ecf6ab7bb2048854bcbe2d4 Mon Sep 17 00:00:00 2001 From: titan-ron <30556071+titan-ron@users.noreply.github.com> Date: Thu, 24 Sep 2026 18:35:35 +0300 Subject: [PATCH 1/4] =?UTF-8?q?feat(about):=20open=20prefilled=20issue=20f?= =?UTF-8?q?orms=20and=20discussions=20from=20Settings=20=E2=80=BA=20About?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Feedback group in About opens GitHub: Report a problem and Sessions missing or wrong prefill the issue form with the Cockpit version, macOS version and architecture, and each agent CLI's version and install method — never paths, accounts or session content. Suggest an idea and Questions & discussion open their forms. AppInfo gains the macOS product version for it. The issue forms, a pull request template and a code of conduct come with it, so the repository's community profile is complete. --- .github/ISSUE_TEMPLATE/bug.yml | 54 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 ++ .github/ISSUE_TEMPLATE/idea.yml | 16 +++ .github/ISSUE_TEMPLATE/sessions.yml | 53 +++++++++ .github/pull_request_template.md | 10 ++ CODE_OF_CONDUCT.md | 130 ++++++++++++++++++++++ design-system/cockpit/pages/settings.md | 13 ++- src/main/updates.ts | 5 +- src/renderer/src/AboutSection.tsx | 87 ++++++++++++++- src/renderer/src/style.css | 3 + src/shared/feedback.ts | 108 +++++++++++++++++++ src/shared/types.ts | 2 + tests/component/settings-about.test.tsx | 64 ++++++++++- tests/component/stub-api.ts | 1 + tests/feedback.test.ts | 138 ++++++++++++++++++++++++ 15 files changed, 690 insertions(+), 5 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/idea.yml create mode 100644 .github/ISSUE_TEMPLATE/sessions.yml create mode 100644 .github/pull_request_template.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 src/shared/feedback.ts create mode 100644 tests/feedback.test.ts diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..e23b4ec4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,54 @@ +name: Report a problem +description: Something in Cockpit is broken, stuck or wrong. +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time. **Settings › About › Report a problem** opens this form + with the version fields already filled in. + Sessions that are missing or show the wrong title, branch or transcript have + [their own form](https://github.com/tashtit/cockpit/issues/new?template=sessions.yml). + - type: textarea + id: what-happened + attributes: + label: What happened + description: What you saw, and what you expected instead. + validations: + required: true + - type: textarea + id: steps + attributes: + label: How to make it happen again + description: The steps, if you know them. "It happened once" is useful too. + placeholder: | + 1. Open a Codex session + 2. Send a message while the agent is working + 3. … + - type: input + id: version + attributes: + label: Cockpit version + description: Settings › About shows it. + placeholder: "0.31.1" + validations: + required: true + - type: input + id: macos + attributes: + label: macOS + placeholder: "26.0 (arm64)" + - type: textarea + id: agents + attributes: + label: Agent CLIs + description: Which agent CLIs you have, and their versions. + placeholder: | + Claude Code: 2.1.236 (Homebrew cask) + Codex: 0.154.0 (npm) + Copilot: not installed + - type: textarea + id: extra + attributes: + label: Anything else + description: Screenshots, or the error text. Please leave out anything private — this issue is public. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..fb59b718 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: true +contact_links: + - name: Ask a question + url: https://github.com/tashtit/cockpit/discussions/categories/q-a + about: How do I…? Does Cockpit…? Ask in Discussions. + - name: Show what you use it for + url: https://github.com/tashtit/cockpit/discussions/categories/show-and-tell + about: Your setup, your workflow, a screenshot — we read every one. + - name: Report a security vulnerability + url: https://github.com/tashtit/cockpit/security/advisories/new + about: Privately, please — not in a public issue. diff --git a/.github/ISSUE_TEMPLATE/idea.yml b/.github/ISSUE_TEMPLATE/idea.yml new file mode 100644 index 00000000..1d58150a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/idea.yml @@ -0,0 +1,16 @@ +name: Suggest an idea +description: Something Cockpit could do, or do better. +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: What you are trying to do + description: The situation, and what gets in the way today. This matters more than the solution. + validations: + required: true + - type: textarea + id: proposal + attributes: + label: What would help + description: How you imagine it working, if you have an idea. diff --git a/.github/ISSUE_TEMPLATE/sessions.yml b/.github/ISSUE_TEMPLATE/sessions.yml new file mode 100644 index 00000000..431b1fbe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sessions.yml @@ -0,0 +1,53 @@ +name: Sessions missing or wrong +description: A session doesn't show up, or shows the wrong title, repo, branch or transcript. +labels: [bug] +body: + - type: markdown + attributes: + value: | + Each agent writes its own session log format, and the formats change between CLI + releases — so the agent and its version are what tell us where to look. + Please don't attach a session log: it holds your conversation. Describe what is + wrong and we'll ask for anything specific. + - type: dropdown + id: agent + attributes: + label: Agent + options: + - Claude Code + - Codex + - Copilot CLI + - Another agent over ACP + validations: + required: true + - type: textarea + id: what-is-wrong + attributes: + label: What is missing or wrong + description: For example "sessions from the last two days don't appear", or "the branch shows main but the session ran on a worktree branch". + validations: + required: true + - type: input + id: version + attributes: + label: Cockpit version + description: Settings › About shows it. + placeholder: "0.31.1" + validations: + required: true + - type: input + id: macos + attributes: + label: macOS + placeholder: "26.0 (arm64)" + - type: textarea + id: agents + attributes: + label: Agent CLIs + description: Which agent CLIs you have, and their versions. + placeholder: | + Claude Code: 2.1.236 (Homebrew cask) + Codex: 0.154.0 (npm) + Copilot: not installed + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..a7cce6a6 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +## What and why + + + +## How it was checked + +- [ ] `npm run typecheck` +- [ ] `npm test` +- [ ] Seen in the running app (`npm run dev`), for anything a person sees +- [ ] `docs/guide/` updated, for anything that changes what a person sees or does diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..56eaf7e7 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,130 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement privately: use +**Report content** from the **…** menu on the issue, pull request, discussion or +comment concerned, which reaches the maintainers of this repository, or contact a +maintainer (@titan-ron, @amitbreuer, @matansocher) through their GitHub profile. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/design-system/cockpit/pages/settings.md b/design-system/cockpit/pages/settings.md index 61a35828..c241854f 100644 --- a/design-system/cockpit/pages/settings.md +++ b/design-system/cockpit/pages/settings.md @@ -29,7 +29,8 @@ seventh. leading `

` — repeating the pill directly under it is noise, and the panel is already named by its tab (`aria-labelledby`). A panel holding more than one group keeps an `h3` per group (Accounts: "Agent accounts & usage", then "GitHub"; View: "History", then - "Display"; Providers: "Model providers", then "ACP agents"). + "Display"; Providers: "Model providers", then "ACP agents"; About: "Updates", then + "Feedback"). - **The tab row is one tab stop.** Roving `tabIndex` (0 on the selected tab, -1 on the rest); ←/→ wrap, Home/End jump to the ends, and moving selects — `TabList` does this for every card view. The card's `h2` still takes focus on mount; picking a tab leaves @@ -249,6 +250,16 @@ seventh. (`openLicenseNotices`); the reason it could not open shows verbatim in a `.new-error role="alert"` under the hint. A development run shows the `unsupported` reason as prose and neither the action nor the switches — this build could not act on them. + Last, the **Feedback** group: one `.feedback-actions` line of four `.btn-ghost.small` + keys that wraps on its own — Report a problem · Sessions missing or wrong · Suggest an + idea · Questions & discussion — opening the repository's issue forms and Discussions + through `openExternal`. The URLs are `feedbackUrl` in `src/shared/feedback.ts`, never + built in the component. The two reports are prefilled with versions only (Cockpit, + macOS + architecture, each agent CLI's version and install method); the CLIs are read + on the click, not when the tab opens, and a read that fails or takes more than a few + seconds opens the form without that field. The pressed key reads "Opening…" and all + four are disabled meanwhile. One `.ns-hint.ns-prose` line under the keys must keep + saying what the form is filled with and that nothing else is. - App-level: the global Escape handler blurs a focused field first and only closes the view on a second press — a habitual Escape must never discard a half-typed path. diff --git a/src/main/updates.ts b/src/main/updates.ts index 10b7f3cc..db56423f 100644 --- a/src/main/updates.ts +++ b/src/main/updates.ts @@ -1,5 +1,6 @@ import { app } from 'electron' import { autoUpdater } from 'electron-updater' +import { COCKPIT_REPO_URL } from '../shared/feedback' import type { AppInfo, UpdateInstallOutcome, UpdatePrefs, UpdateState } from '../shared/types' import { updatePrefs } from './config' import { @@ -15,7 +16,7 @@ import { import { checkOutcome, pickZip, type CheckResult, type FeedFile } from './update-install-core' /** Where releases live — the updater's feed and the only place release notes are kept. */ -export const RELEASES_URL = 'https://github.com/tashtit/cockpit/releases' +export const RELEASES_URL = `${COCKPIT_REPO_URL}/releases` /** The launch check waits for the index to settle; afterwards a quiet periodic one. */ const FIRST_CHECK_DELAY_MS = 20_000 @@ -284,6 +285,8 @@ export function appInfo(): AppInfo { version: app.getVersion(), packaged: app.isPackaged, platform: process.platform, + // Electron's own reading of the product version; os.release() is Darwin's + osVersion: process.getSystemVersion(), arch: process.arch, electron: process.versions.electron ?? '', releasesUrl: RELEASES_URL diff --git a/src/renderer/src/AboutSection.tsx b/src/renderer/src/AboutSection.tsx index 3676d025..f8fd60e2 100644 --- a/src/renderer/src/AboutSection.tsx +++ b/src/renderer/src/AboutSection.tsx @@ -1,4 +1,10 @@ import { useEffect, useState, type JSX } from 'react' +import { + feedbackPrefills, + feedbackUrl, + type FeedbackCli, + type FeedbackKind +} from '../../shared/feedback' import type { AppInfo, UpdatePrefs, UpdateState } from '../../shared/types' import { api } from './api' import { fmtAgo } from './format' @@ -23,6 +29,82 @@ const UPDATE_SWITCHES: ReadonlyArray<{ } ] +const FEEDBACK_ACTIONS: ReadonlyArray<{ readonly kind: FeedbackKind; readonly label: string }> = [ + { kind: 'bug', label: 'Report a problem' }, + { kind: 'sessions', label: 'Sessions missing or wrong' }, + { kind: 'idea', label: 'Suggest an idea' }, + { kind: 'discussion', label: 'Questions & discussion' } +] + +/** How long a report waits on the CLI versions before it opens without them. */ +const CLI_WAIT_MS = 4_000 + +/** + * The agent CLIs as the Accounts tab reads them, for a report's prefill — or null + * when they can't be read in time, which leaves that field to the person rather than + * holding the click on a slow `brew` or an offline registry. + */ +function cliFacts(): Promise { + return new Promise((resolve) => { + const timer = setTimeout(() => resolve(null), CLI_WAIT_MS) + api + .listCliStatus(false) + .then(resolve, () => resolve(null)) + .finally(() => clearTimeout(timer)) + }) +} + +/** + * Feedback: the repository's issue forms and discussions, one click away. The two + * reports open prefilled with versions only (`src/shared/feedback.ts` decides what + * that is); the CLIs are asked on the click, never when the tab opens. + */ +function FeedbackGroup({ + appInfo, + onStatus +}: { + appInfo: AppInfo | null + onStatus: (s: string) => void +}): JSX.Element { + const [opening, setOpening] = useState(null) + + const open = async (kind: FeedbackKind, label: string): Promise => { + if (opening) return + setOpening(kind) + try { + const clis = feedbackPrefills(kind) ? await cliFacts() : null + await api.openExternal(feedbackUrl(kind, { app: appInfo, clis })) + onStatus(`${label} opened on GitHub`) + } catch (err) { + onStatus(`Could not open GitHub: ${ipcErrorText(err)}`) + } finally { + setOpening(null) + } + } + + return ( + <> +

Feedback

+
+ {FEEDBACK_ACTIONS.map((a) => ( + + ))} +
+

+ Opens GitHub. Reports come with your Cockpit, macOS and agent CLI versions filled in — + nothing else. +

+ + ) +} + /** The About row's one-line readout of where the updater stands. */ export function updateLine(u: UpdateState | null, prefs: UpdatePrefs | null): string { if (!u) return 'loading…' @@ -53,7 +135,8 @@ export function updateLine(u: UpdateState | null, prefs: UpdatePrefs | null): st } /** - * The About tab: what this build is, and the whole of the updater's control surface. + * The About tab: what this build is, the whole of the updater's control surface, and + * where feedback goes. * * The update state itself is the shell's, not this tab's — main pushes transitions * whether or not About is the tab on screen, and the card's status region has to @@ -170,6 +253,7 @@ export function AboutSection({ return ( <> +

Updates