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
119 changes: 119 additions & 0 deletions .github/workflows/discord.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -514,3 +514,122 @@ jobs:
const txt = await res.text();
core.setFailed(`Leaderboard post failed ${res.status}: ${txt}`);
}

# Posts a notification to the #🗺️・roadmap Discord channel whenever a PR
# changes ROADMAP.md (the public roadmap file). This mirrors the way the
# `notify` job above syncs PR activity to the 🔎・pr-reviews channel —
# the user-facing community gets pinged in Discord when the roadmap moves.
#
# Required secret:
# DISCORD_ROADMAP_WEBHOOK_URL — webhook URL of the #🗺️・roadmap channel
# Optional (re-used from the existing notify job):
# DISCORD_WEBHOOK_USERNAME, DISCORD_WEBHOOK_AVATAR_URL
roadmap-notify:
if: github.event_name == 'pull_request_target'
concurrency:
group: discord-roadmap-sync-${{ github.repository }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
runs-on: ubuntu-latest
steps:
- name: Post roadmap change to #🗺️・roadmap Discord channel
continue-on-error: true
uses: actions/github-script@v7
env:
DISCORD_ROADMAP_WEBHOOK_URL: ${{ secrets.DISCORD_ROADMAP_WEBHOOK_URL }}
DISCORD_WEBHOOK_USERNAME: ${{ secrets.DISCORD_WEBHOOK_USERNAME }}
DISCORD_WEBHOOK_AVATAR_URL: ${{ secrets.DISCORD_WEBHOOK_AVATAR_URL }}
with:
script: |
const ROADMAP_PATTERN = /(^|\/)ROADMAP\.md$|(^|\/)docs\/roadmap\.md$/i;
const VALID_ACTIONS = ["opened", "reopened", "synchronize", "closed"];

const pr = context.payload.pull_request;
if (!pr) {
core.info("No PR context; skipping roadmap notify.");
return;
}

const action = context.payload.action || "";
if (!VALID_ACTIONS.includes(action)) {
core.info(`Skipping roadmap notify for action '${action}'.`);
return;
}

let files;
try {
const res = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
per_page: 100,
});
files = res.data;
} catch (err) {
core.warning(`Failed to list PR files: ${err && err.message ? err.message : err}`);
return;
}

const roadmapFiles = files.filter(f => ROADMAP_PATTERN.test(f.filename));
if (roadmapFiles.length === 0) {
core.info("No roadmap files in PR; skipping roadmap notify.");
return;
}

const webhookUrl = (process.env.DISCORD_ROADMAP_WEBHOOK_URL || "").trim();
if (!webhookUrl) {
core.info("DISCORD_ROADMAP_WEBHOOK_URL not set; skipping roadmap notify. Add it as a repo secret to enable #🗺️・roadmap auto-posts.");
return;
}

const webhookUsername = (process.env.DISCORD_WEBHOOK_USERNAME || "OpenScreen").trim();
const webhookAvatar = (process.env.DISCORD_WEBHOOK_AVATAR_URL || "").trim();

const fileList = roadmapFiles.map(f => {
const icon = f.status === "added" ? "🆕" : f.status === "removed" ? "🗑️" : "✏️";
return `${icon} \`${f.filename}\` (+${f.additions}/-${f.deletions})`;
}).join("\n");

const isMerged = action === "closed" && !!pr.merged;
const titlePrefix = isMerged ? "✅ Roadmap merged" : "🗺️ Roadmap updated";
const color = isMerged ? 5763719 : 1998671;
const status = pr.draft ? "Draft" : (isMerged ? "Merged" : (action === "closed" ? "Closed (not merged)" : "Open"));

const description = (pr.body || "No description provided.").trim() || "No description provided.";

const payload = {
username: webhookUsername,
...(webhookAvatar ? { avatar_url: webhookAvatar } : {}),
content: `${titlePrefix} via PR #${pr.number}`,
embeds: [
{
title: `${pr.title} • #${pr.number}`,
url: pr.html_url,
description: description.length > 1800 ? description.slice(0, 1799) + "…" : description,
color,
fields: [
{ name: "Roadmap files", value: fileList, inline: false },
{ name: "Author", value: `[${pr.user?.login || "unknown"}](${pr.user?.html_url || pr.html_url})`, inline: true },
{ name: "Status", value: status, inline: true },
],
footer: { text: `${context.repo.owner}/${context.repo.repo}` },
timestamp: new Date().toISOString(),
},
],
allowed_mentions: { parse: [] },
};

try {
const res = await fetch(`${webhookUrl}?wait=true`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) {
const txt = await res.text();
core.warning(`Roadmap Discord notify failed ${res.status}: ${txt}`);
return;
}
core.info(`Roadmap Discord notify posted for PR #${pr.number} (${roadmapFiles.length} file(s)).`);
} catch (err) {
core.warning(`Roadmap Discord notify threw: ${err && err.message ? err.message : err}`);
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ OpenScreen is community-driven. If you need help, want to report a bug, or just

- 💬 **Discord** — [Join the OpenScreen Discord](https://discord.gg/VvT6Vtnyh) for real-time help, showcase, and discussion
- 🐞 **[GitHub Issues](https://github.com/EtienneLescot/openscreen/issues)** — bug reports and feature requests
- 🗺️ **[Roadmap](./ROADMAP.md)** — see what we're building next

---

Expand Down
53 changes: 53 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# OpenScreen Roadmap
The recorder you love, with an optional AI sidekick. Same sleek, low-friction recorder UX. An opt-in AI editing layer is on the way for users who want it — never required, never snuck in.

This roadmap is the source of truth for what we're shipping next in OpenScreen. It is a living document — items move between tiers as work lands. Have an idea, a vote, or a dissenting opinion? Drop into the 🗺️・roadmap channel on our Discord or open a GitHub issue with the `roadmap` label.

## 🧭 North Star
**Record → Edit → Export.** (with an optional AI shortcut for users who want one)

OpenScreen is, first and foremost, a polished screen recorder. Record, trim on the timeline, export. Most users will keep using exactly this workflow.

We're also exploring an optional AI editing layer — for users who want to edit by talking or by editing a transcript. It's opt-in, off by default, and never required. If you don't enable it, the AI layer doesn't exist for your install: nothing downloads, nothing leaves your machine, no LLM is contacted.

Three axes guide every decision on this roadmap:

- **Stability first** — the recorder must work reliably on macOS, Windows, and Linux. Bugs found by real users ship before new features.
- **Sleek UX stays** — every AI feature must keep the OpenScreen feel: minimal clicks, instant feedback, no clutter.
- **100% free, forever** — no paywalls, no premium tier, no usage caps. Every feature on this page ships under MIT.

## 🤖 Direction — the optional AI Edition
A Screen Studio + Descript clone, open-source and free forever. The recorder-first UX stays intact, and the AI layer sits beside it, off by default.

Capabilities we're exploring (each one opt-in, each one toggleable independently):

- **Local Whisper transcription (opt-in, on-device)** — OpenScreen already ships on-device Whisper transcription for automatic captions. This extends that foundation: the same local transcript powers the editing features below, with no upload, no cloud, no extra setup required.
- **Transcript-driven editing (opt-in, local)** — edit video like a doc (Descript-style: delete a word, cut the span). Works with the local transcript; no cloud needed.
- **One-click cleanup (opt-in, local)** — filler-word removal, silence trimming, Studio Sound voice enhancement. All on-device.
- **Edit by chat (opt-in, requires BYO LLM key)** — say "cut the part where I repeat myself between 0:42 and 1:10" and the agent applies a structured timeline operation. Off until you connect a provider.
- **Non-destructive project document (always on)** — every edit, AI or manual, is undoable; the timeline is always recoverable.
- **Bring-your-own LLM (opt-in)** — OpenAI, Anthropic, Google, Mistral, OpenRouter, GitHub Copilot, OpenAI-compatible endpoints, ChatGPT account auth. You choose; we never see your keys or your data.

This section is a direction, not a sprint plan. Concrete items land here as RFCs once the recorder is stable enough to build on top of.

## 🛠️ Stability & quality (what we're actually shipping)
Pulled from real user bug reports on getopenscreen/openscreen. This is the queue for the next release window.

- [ ] **Fix:** video disappears from editor after export — [#8](../../issues/8) (Linux, Manjaro). Renderer regression after export.
- [ ] **Fix:** crash after stopping macOS recording — [#21](../../issues/21) (macOS 26.4.1, Apple Silicon). Crash is in the Electron / Node async fs shutdown path; recording artifacts are written correctly.
- [ ] **Fix:** macOS cursor offset in single-window capture — [#22](../../issues/22).
- [ ] **Fix:** recover preview from WebGL context loss on Linux / Wayland — [#19](../../issues/19).
- [ ] **Feature:** software H.264 fallback when no GPU encoder MFT is available — [#18](../../issues/18). Critical for VMs, broken-driver machines, and headless environments.
- [ ] **Feature:** copy / paste attributes & effects in the timeline — [#24](../../issues/24). Right-click menu + standard Ctrl/Cmd+C / Ctrl/Cmd+V shortcuts.

## 📬 How to influence this roadmap
- **Discord** — join the OpenScreen Discord and post in [#🗺️・roadmap](). The fastest way to get a thumbs-up or thumbs-down on a feature.
- **GitHub** — open an issue with the `enhancement` label, or react with 👍 / 👎 on existing items.
- **PRs** — if you want to ship one of these, open a PR and link the relevant issue. We review fast and help with native-bridge / i18n questions.

Anything not on this list yet? Open an issue and tag it `roadmap` — we'll triage it into a tier within a week.

---

## Changelog
- **2026-06-24** — initial draft. Stability items pulled from open issues / PRs on getopenscreen/openscreen. AI section presented as opt-in / off by default. Whisper entry updated to reflect existing caption feature.
Loading