Skip to content

Commit 234025f

Browse files
katipallyclaude
andauthored
feat(mac): first-launch welcome window + v2.7.2 (#6)
* chore(oss): add contribution automation + reframe README sleep line Set up the "recommended" contribution-management bundle so incoming issues and PRs get routed and triaged with less manual work: - CODEOWNERS: auto-requests @katipally review on every PR. - labeler: auto-tags PRs by area (docs / ci / mac / ios / core). - greetings: welcomes first-time issue and PR authors. - stale: nudges then closes issues/PRs quiet for 60+14 days, exempting pinned / security / help wanted / good first issue. - release-drafter: keeps a draft changelog of the next release from merged PRs (drafts only; release.yml still publishes on a v* tag). Also reframe the README "Mac falls asleep" line: the run doesn't lose progress out of nowhere, it stalls because sleep drops the network and suspends the agent process. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(mac): surface the open/close shortcut across the app The Option+Space (rebindable) shortcut to open/close the panel was only visible in Settings. Surface it where users actually look, using the live GlobalHotkey.shared.current.descriptionForUI so it always reflects the current binding (not a hardcoded key): - Menu bar dropdown: the "Open Doom Coder" item shows the shortcut. - Menu bar icon tooltip: adds a "<shortcut> to open or close" line. - Panel header: a small shortcut pill next to the title, with a hover tip. - About window: an "Open / close shortcut" row. Verified with a Debug build (BUILD SUCCEEDED) and SwiftLint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(mac): first-launch welcome window + v2.7.2 Add a short, plain-language onboarding window shown once on a fresh install: how to open Doom Coder (⌥ Space), a one-tap notifications opt-in, and an optional Accessibility note. Hosted as its own NSWindow so it reliably appears on first launch (the SwiftUI window-opener bridge isn't mounted until the floating panel is first shown). Upgraders are detected via existing What's New flags and skip onboarding. README: restyle the macOS download as a clickable tile, label macOS as the app vs iOS as the companion, surface ⌥ Space up top, add per-platform latest-version badges, and cache the downloads badge. Bump version to 2.7.2 (macOS-only release). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(readme): cache all dynamic badges to survive shields.io outages Add cacheSeconds=3600 to the Release, CI, and Stars badges (Downloads and the per-platform version badges already had it). Dynamic GitHub/ iTunes badges occasionally render shields.io's 'Unable to select next GitHub token from pool' error during their API rate-limit windows; caching makes shields serve the last-good value instead. Static /badge/ shields (license, macOS, iOS, Swift) make no API call and are left as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(release-drafter): run only on push to main The release-drafter action reads its config from the default branch, so a PR that introduces .github/release-drafter.yml (as this branch does) fails the check because main doesn't have the config yet. The config is drafting-only with no autolabeler, so it has nothing to do on a pull_request regardless. Drop the pull_request trigger — drafting runs as PRs merge into main, which is the documented intent — and the false PR failure goes away. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2851163 commit 234025f

15 files changed

Lines changed: 542 additions & 25 deletions

.github/CODEOWNERS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Code owners for Doom Coder.
2+
#
3+
# GitHub auto-requests a review from the owner(s) listed here whenever a pull
4+
# request touches matching files. Right now one person maintains the whole
5+
# project, so the default owner covers everything. As the project grows, add
6+
# more specific lines below (more specific paths win over the catch-all).
7+
8+
# Default owner for everything in the repo.
9+
* @katipally

.github/labeler.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Auto-labels pull requests based on which files they touch.
2+
# Used by .github/workflows/labeler.yml (actions/labeler v5 syntax).
3+
# The labels below must exist in the repo (Settings > Labels).
4+
5+
documentation:
6+
- changed-files:
7+
- any-glob-to-any-file:
8+
- "**/*.md"
9+
- "docs/**"
10+
11+
ci:
12+
- changed-files:
13+
- any-glob-to-any-file:
14+
- ".github/**"
15+
16+
mac:
17+
- changed-files:
18+
- any-glob-to-any-file:
19+
- "DoomCoder/**"
20+
- "dc-hook/**"
21+
22+
ios:
23+
- changed-files:
24+
- any-glob-to-any-file:
25+
- "DoomCoderCompanion/**"
26+
27+
core:
28+
- changed-files:
29+
- any-glob-to-any-file:
30+
- "Packages/**"

.github/release-drafter.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Config for Release Drafter (see .github/workflows/release-drafter.yml).
2+
#
3+
# As pull requests merge into main, this keeps a DRAFT GitHub Release updated
4+
# with a categorized changelog, grouped by the PR's labels. It never publishes
5+
# anything. The real release is still cut by release.yml when you push a v* tag.
6+
# Use the draft as a ready-made changelog to paste into docs/CHANGELOG.md and
7+
# the published release, then delete the draft.
8+
9+
name-template: "v$RESOLVED_VERSION"
10+
tag-template: "v$RESOLVED_VERSION"
11+
12+
categories:
13+
- title: "Features"
14+
labels:
15+
- "enhancement"
16+
- "agent-support"
17+
- title: "Bug Fixes"
18+
labels:
19+
- "bug"
20+
- title: "Documentation"
21+
labels:
22+
- "documentation"
23+
- title: "Maintenance"
24+
labels:
25+
- "ci"
26+
- "dependencies"
27+
- "github_actions"
28+
29+
change-template: "- $TITLE (#$NUMBER) @$AUTHOR"
30+
change-title-escapes: '\<*_&'
31+
32+
version-resolver:
33+
major:
34+
labels:
35+
- "major"
36+
minor:
37+
labels:
38+
- "enhancement"
39+
- "agent-support"
40+
patch:
41+
labels:
42+
- "bug"
43+
- "documentation"
44+
- "ci"
45+
- "dependencies"
46+
default: patch
47+
48+
exclude-labels:
49+
- "skip-changelog"
50+
51+
template: |
52+
## What's changed
53+
54+
$CHANGES
55+
56+
**Full changelog**: https://github.com/katipally/Doom-Coder/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION

.github/workflows/greetings.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Greetings
2+
3+
# Posts a friendly note the first time someone opens an issue or a pull
4+
# request. Helps new contributors feel welcome and points them at the docs.
5+
6+
on:
7+
issues:
8+
types: [opened]
9+
pull_request_target:
10+
types: [opened]
11+
12+
permissions:
13+
issues: write
14+
pull-requests: write
15+
16+
jobs:
17+
greeting:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/first-interaction@v1
21+
with:
22+
repo-token: ${{ secrets.GITHUB_TOKEN }}
23+
issue-message: |
24+
Thanks for opening your first issue, and welcome to Doom Coder.
25+
26+
A quick check that helps us move fast: make sure you are on the
27+
latest version and that the title says what is going on in a few
28+
words. If this is a security problem, please close this and follow
29+
SECURITY.md instead. We will take a look soon.
30+
pr-message: |
31+
Thanks for your first pull request, and welcome to Doom Coder.
32+
33+
CI will run automatically (Mac build, iOS build, SwiftLint) and all
34+
three need to pass. If anything looks red, open the run to see why.
35+
A maintainer will review when they can. If you have not already,
36+
skim CONTRIBUTING.md so the review goes quickly.

.github/workflows/labeler.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Labeler
2+
3+
# Tags pull requests with area labels (docs, ci, mac, ios, core) based on the
4+
# files they change. Rules live in .github/labeler.yml.
5+
6+
on:
7+
pull_request_target:
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
label:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/labeler@v5
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Release Drafter
2+
3+
# Keeps a draft GitHub Release up to date with the changelog for the next
4+
# version as PRs merge into main. Drafts only, never publishes. Config lives in
5+
# .github/release-drafter.yml. The actual release is published by release.yml
6+
# on a v* tag push.
7+
8+
# Runs only on push to main: release-drafter reads its config from the
9+
# default branch, so a PR introducing or changing the config can't be
10+
# evaluated until it lands on main. This config is drafting-only (no
11+
# autolabeler), so there's nothing for it to do on a pull_request anyway.
12+
on:
13+
push:
14+
branches:
15+
- main
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
update_release_draft:
22+
permissions:
23+
contents: write
24+
pull-requests: read
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: release-drafter/release-drafter@v6
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/stale.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Stale
2+
3+
# Nudges issues and pull requests that have gone quiet, then closes them if
4+
# nothing happens. Keeps the tracker honest without anyone policing it by hand.
5+
# Anything labeled pinned, security, help wanted, or good first issue is left
6+
# alone. Runs once a day.
7+
8+
on:
9+
schedule:
10+
- cron: "30 1 * * *"
11+
workflow_dispatch:
12+
13+
permissions:
14+
issues: write
15+
pull-requests: write
16+
17+
jobs:
18+
stale:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/stale@v9
22+
with:
23+
days-before-stale: 60
24+
days-before-close: 14
25+
stale-issue-label: stale
26+
stale-pr-label: stale
27+
exempt-issue-labels: "pinned,security,help wanted,good first issue"
28+
exempt-pr-labels: "pinned,security"
29+
stale-issue-message: |
30+
This issue has been quiet for 60 days, so it is marked stale. If it
31+
still matters, drop a comment and we will keep it open. Otherwise it
32+
will close in 14 days.
33+
stale-pr-message: |
34+
This pull request has been quiet for 60 days, so it is marked stale.
35+
Comment or push an update to keep it open. Otherwise it will close in
36+
14 days. No hard feelings, you can always reopen it.
37+
close-issue-message: |
38+
Closing this out after no activity. Comment to reopen if it is still
39+
relevant.
40+
close-pr-message: |
41+
Closing this out after no activity. Reopen any time if you want to
42+
pick it back up.

DoomCoder.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
DC0000000000002000000F03 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F03 /* AppRouter.swift */; };
6969
DC0000000000002000000F04 /* SymbolCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F04 /* SymbolCatalog.swift */; };
7070
DC0000000000002000000F05 /* WhatsNewHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F05 /* WhatsNewHost.swift */; };
71+
DC0000000000002000000F50 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F50 /* WelcomeView.swift */; };
7172
DC0000000000002000000F06 /* RainbowIdleGlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F06 /* RainbowIdleGlow.swift */; };
7273
DC0000000000002000000F10 /* LiveEventRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F10 /* LiveEventRow.swift */; };
7374
DC0000000000002000000F11 /* ConnectionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0000000000001000000F11 /* ConnectionsCard.swift */; };
@@ -143,6 +144,7 @@
143144
DC0000000000001000000F03 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
144145
DC0000000000001000000F04 /* SymbolCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolCatalog.swift; sourceTree = "<group>"; };
145146
DC0000000000001000000F05 /* WhatsNewHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewHost.swift; sourceTree = "<group>"; };
147+
DC0000000000001000000F50 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
146148
DC0000000000001000000F06 /* RainbowIdleGlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RainbowIdleGlow.swift; sourceTree = "<group>"; };
147149
DC0000000000001000000F10 /* LiveEventRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveEventRow.swift; sourceTree = "<group>"; };
148150
DC0000000000001000000F11 /* ConnectionsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsCard.swift; sourceTree = "<group>"; };
@@ -191,6 +193,7 @@
191193
DC0000000000001000000F03 /* AppRouter.swift */,
192194
DC0000000000001000000F04 /* SymbolCatalog.swift */,
193195
DC0000000000001000000F05 /* WhatsNewHost.swift */,
196+
DC0000000000001000000F50 /* WelcomeView.swift */,
194197
DC0000000000001000000F06 /* RainbowIdleGlow.swift */,
195198
DC0000000000001000000003 /* SleepManager.swift */,
196199
DC0000000000001000000052 /* ToolsWindow.swift */,
@@ -400,6 +403,7 @@
400403
DC0000000000002000000F03 /* AppRouter.swift in Sources */,
401404
DC0000000000002000000F04 /* SymbolCatalog.swift in Sources */,
402405
DC0000000000002000000F05 /* WhatsNewHost.swift in Sources */,
406+
DC0000000000002000000F50 /* WelcomeView.swift in Sources */,
403407
DC0000000000002000000F06 /* RainbowIdleGlow.swift in Sources */,
404408
DC0000000000002000000009 /* CheckForUpdatesViewModel.swift in Sources */,
405409
DC0000000000002000000014 /* ConfigureSettingsPane.swift in Sources */,
@@ -582,7 +586,7 @@
582586
CODE_SIGN_IDENTITY = "Apple Development";
583587
CODE_SIGN_STYLE = Automatic;
584588
COMBINE_HIDPI_IMAGES = YES;
585-
CURRENT_PROJECT_VERSION = 2701;
589+
CURRENT_PROJECT_VERSION = 2702;
586590
DEVELOPMENT_TEAM = A9P2388PHM;
587591
ENABLE_HARDENED_RUNTIME = YES;
588592
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -592,7 +596,7 @@
592596
"$(inherited)",
593597
"@executable_path/../Frameworks",
594598
);
595-
MARKETING_VERSION = 2.7.1;
599+
MARKETING_VERSION = 2.7.2;
596600
PRODUCT_BUNDLE_IDENTIFIER = com.doomcoder.app;
597601
PRODUCT_NAME = "$(TARGET_NAME)";
598602
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -610,7 +614,7 @@
610614
CODE_SIGN_IDENTITY = "Apple Development";
611615
CODE_SIGN_STYLE = Automatic;
612616
COMBINE_HIDPI_IMAGES = YES;
613-
CURRENT_PROJECT_VERSION = 2701;
617+
CURRENT_PROJECT_VERSION = 2702;
614618
DEVELOPMENT_TEAM = A9P2388PHM;
615619
ENABLE_HARDENED_RUNTIME = YES;
616620
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -620,7 +624,7 @@
620624
"$(inherited)",
621625
"@executable_path/../Frameworks",
622626
);
623-
MARKETING_VERSION = 2.7.1;
627+
MARKETING_VERSION = 2.7.2;
624628
PRODUCT_BUNDLE_IDENTIFIER = com.doomcoder.app;
625629
PRODUCT_NAME = "$(TARGET_NAME)";
626630
PROVISIONING_PROFILE_SPECIFIER = "";

DoomCoder/AboutView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct AboutView: View {
4343
Form {
4444
LabeledContent("Version", value: version)
4545
LabeledContent("Build", value: build)
46+
LabeledContent("Open / close shortcut", value: GlobalHotkey.shared.current.descriptionForUI)
4647
LabeledContent("Website", value: "github.com/katipally/Doom-Coder")
4748
}
4849
.formStyle(.grouped)

DoomCoder/DoomCoderApp.swift

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,31 @@ final class DoomCoderAppDelegate: NSObject, NSApplicationDelegate, UNUserNotific
163163
_ = SleepManager.shared
164164
}
165165

166-
// Show the highest-version unseen What's New sheet (if any).
167-
// `WhatsNewHost` picks the right one and advances on dismiss.
168-
// The Window scene is `.defaultLaunchBehavior(.suppressed)` so it
169-
// never auto-opens — we open it explicitly here.
170-
if WhatsNewVersion.highestUnseen() != nil {
171-
Task { @MainActor in
166+
// First-launch onboarding vs. What's New. A fresh install gets the
167+
// welcome window (its own NSWindow, see WelcomeWindowController);
168+
// everyone else falls through to the usual suppressed What's New scene.
169+
Task { @MainActor in
170+
if !OnboardingState.welcomeSeen {
171+
if OnboardingState.looksLikeExistingUser {
172+
// Upgrader on a machine that predates onboarding — they
173+
// already know the ropes. Mark it seen, fall through to
174+
// the usual What's New flow below.
175+
OnboardingState.markWelcomeSeen()
176+
} else {
177+
// Genuinely fresh install: show the welcome window and
178+
// suppress the legacy What's New sheets a new user has no
179+
// context for. `WelcomeView` marks the flag on appear.
180+
for v in WhatsNewVersion.allCases {
181+
UserDefaults.standard.set(true, forKey: v.defaultsKey)
182+
}
183+
WelcomeWindowController.shared.show()
184+
return
185+
}
186+
}
187+
188+
// Show the highest-version unseen What's New sheet (if any).
189+
// `WhatsNewHost` picks the right one and advances on dismiss.
190+
if WhatsNewVersion.highestUnseen() != nil {
172191
WindowOpener.open(.whatsNew)
173192
}
174193
}

0 commit comments

Comments
 (0)