feat(deep-link): implement Emdash GitHub quick link functionality:#1562
feat(deep-link): implement Emdash GitHub quick link functionality:#1562yashdev9274 wants to merge 1 commit intogeneralaction:mainfrom
Conversation
- Added a new protocol for deep linking to GitHub repositories. - Implemented a Cloudflare worker to handle redirects from Netlify. - Created a modal for users to clone repositories directly from quick links. - Enhanced the main application to register and handle deep links. - Updated the preload script to expose new IPC methods for deep link handling. - Added necessary components and hooks to manage quick link interactions in the UI. - Updated package.json to include the new protocol definition.
|
@yashdev9274 is attempting to deploy a commit to the General Action Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
📝 WalkthroughWalkthroughIntroduces a complete deep-link feature enabling users to clone GitHub repositories directly from external URLs via a custom Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant WebBrowser as Web Browser
participant Redirect as Cloudflare<br/>Worker
participant OS as OS Protocol<br/>Handler
participant Electron as Electron<br/>Main
participant Service as DeepLink<br/>Service
participant DB as Project<br/>Database
participant Renderer as Renderer<br/>Process
User->>WebBrowser: Click emdash.github.com/owner/repo
WebBrowser->>Redirect: GET /:owner/:repo
Redirect->>OS: 302 Redirect to<br/>emdash-github://owner/repo
OS->>Electron: Launch/Activate with protocol
Electron->>Service: handleQuickLink(url)
Service->>Service: Parse owner/repo from URL
Service->>DB: findExistingProject(owner, repo)
alt Project exists
DB-->>Service: projectPath
Service->>Renderer: Send deep-link:open-project
Renderer->>Renderer: Activate project view
else Project does not exist
DB-->>Service: null
Service->>Renderer: Send deep-link:clone<br/>{owner, repo, repoUrl}
Renderer->>Renderer: Show QuickLinkModal
end
sequenceDiagram
participant Modal as QuickLink<br/>Modal
participant IPC as IPC Handler<br/>(github:quickLinkClone)
participant Service as GitHub<br/>Service
participant FS as File<br/>System
participant Renderer as Renderer<br/>Process
Modal->>Modal: User clicks Clone
Modal->>IPC: githubQuickLinkClone({owner, repo, repoUrl})
IPC->>IPC: Resolve projects directory<br/>(settings or ~/emdash-projects)
IPC->>Service: cloneRepository(repoUrl,<br/>projectDir/repo)
Service->>FS: Clone repository
FS-->>Service: success/error
alt Clone successful
Service-->>IPC: {success: true}
IPC-->>Modal: {success: true, projectPath}
Modal->>Renderer: Send deep-link:project-cloned
Renderer->>Renderer: Open cloned project
else Clone failed
Service-->>IPC: {error: message}
IPC-->>Modal: {success: false, error}
Modal->>Modal: Display error message
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment Tip You can customize the tone of the review comments and chat replies.Configure the |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/main.ts (1)
148-156:⚠️ Potential issue | 🔴 CriticalRemove duplicate second-instance and requestSingleInstanceLock registrations; capture deep-link URLs at startup before window creation.
second-instanceis registered twice (lines 148–156 and 374–386), causing the same deep-link URL to be processed twice. More critically,requestSingleInstanceLock()is called unconditionally at line 373 insideapp.whenReady(), overriding the production-only protection at lines 138–145. This breaks dev mode by forcing single-instance behavior even whenisDevis true and the lock should be skipped entirely.The
open-urlhandler at line 371 is registered too late (after window creation), and there is no initialprocess.argvcapture for the first app instance. Consolidate to: (1) request the lock once at startup with the existingisDevguard, (2) register handlers once at module level, and (3) capture any initial deep-link URL fromprocess.argvupfront and queue it for handling after the main window is ready.Also applies to: 374–386
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/main.ts` around lines 148 - 156, Remove the duplicate second-instance registration and the unconditional requestSingleInstanceLock call: ensure requestSingleInstanceLock() is only invoked once at startup guarded by the existing isDev check (do not call inside app.whenReady()), register app.on('second-instance', ...) and app.on('open-url', ...) once at module-level, and capture any initial deep-link URL from process.argv immediately on startup (before window creation) so it can be queued and passed to handleDeepLinkUrl(url) after the main window is ready; update the second-instance handler (the existing app.on('second-instance', ...) and the open-url handler) to only restore/focus the BrowserWindow and call handleDeepLinkUrl for the received URL, and remove the duplicate block and the late open-url registration in app.whenReady so deep-links aren’t processed twice or missed for the first instance.
🧹 Nitpick comments (1)
src/main/preload.ts (1)
531-558: Use one source of truth for the preload bridge types.These methods were added to
exposeInMainWorldand tosrc/renderer/types/electron-api.d.ts, but the exportedElectronAPIinterface at Line 882 still omits them. Keeping the bridge surface in multiple declarations is already drifting, and each new IPC method makes the mismatch easier to miss.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/preload.ts` around lines 531 - 558, The ElectronAPI interface is missing the newly added bridge methods/listeners (e.g., onDeepLinkClone, onDeepLinkOpenProject, onDeepLinkProjectCloned, githubIsAuthenticated, githubGetStatus, githubGetUser, githubGetRepositories, githubCloneRepository, githubQuickLinkClone); update the exported ElectronAPI interface to include these exact members with matching function signatures and return types (listener registration methods returning a cleanup function, invoke methods returning appropriate Promise types or typed results) so the preload bridge and type declaration are a single source of truth—ensure the types for the listener payloads ({ owner, repo, repoUrl }, { projectPath }) and github method parameters (repoUrl: string, localPath: string, params: { owner: string; repo: string; repoUrl }) match the implementations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@infra/netlify-redirect/_redirects`:
- Line 1: The redirect currently hardcodes the personal Cloudflare workers host
string "emdash-github-redirect.yashdev-yvd.workers.dev" in the rule that begins
"//:owner/:repo/*"; replace this literal with an environment/config-driven host
(e.g., an org-owned/custom domain or a NETLIFY_REDIRECT_HOST variable) so the
redirect target is configurable per environment and not tied to a developer's
personal workers.dev subdomain. Ensure the redirect rule uses that config value
(or a build-time placeholder) instead of the hardcoded hostname.
- Line 1: The redirect source pattern is incorrectly using a literal double
slash; replace the leading "//:owner/:repo/*" with a single-leading-slash
pattern "/:owner/:repo/*" so normal repo URLs like "/owner/repo/..." match; keep
the destination unchanged
(https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo) and
preserve the 200! status.
In `@infra/netlify-redirect/index.html`:
- Around line 5-10: The fallback writes to document.body.innerHTML from a script
that runs in head, which can be null; modify the logic around the path check in
the inline script so that if path.length < 2 you wait for the DOM to be ready
(e.g., wrap the fallback in a DOMContentLoaded callback or check for
document.body and defer until available) before setting document.body.innerHTML,
keeping the existing redirect behavior (window.location.href =
`emdash-github://${path[0]}/${path[1]}`) unchanged.
In `@src/main/ipc/githubIpc.ts`:
- Around line 474-479: The clone destination is currently just projectDir + repo
which causes collisions across different owners; change localPath to include the
repo owner (e.g., projectDir/owner/repo) before calling
githubService.cloneRepository. Locate where getAppSettings(), projectDir,
localPath and githubService.cloneRepository are used and derive the owner (from
repoUrl or available repo metadata) and use path.join(projectDir, owner, repo)
for localPath so clones are scoped per owner.
In `@src/main/services/DeepLinkServices.ts`:
- Around line 30-38: The findExistingProject method only matches remotes that
look like https URLs; update it to normalize stored remotes before comparing to
buildGitHubUrl(owner, repo) by adding a helper (e.g., normalizeGitRemote) that:
strips protocols (https://, ssh://), strips "git@" and any userinfo, converts
"git@github.com:owner/repo(.git)" and "ssh://git@github.com/owner/repo(.git)"
into the canonical "https://github.com/owner/repo" form, removes trailing
".git", and lowercases the result; then use that normalized remote in the
projects.find comparison inside findExistingProject so all common Git remote
formats match the built GitHub URL.
- Around line 80-83: localPath currently builds as path.join(projectDir, repo)
which causes different owners with the same repo name to collide; change the
local path construction in DeepLinkServices (where settings, projectDir and
localPath are defined) to include the repository owner (e.g., incorporate the
owner variable into the path such as path.join(projectDir, owner, repo) or a
stable owner-repo filename like `${owner}-${repo}`) so each checkout is unique
and matches the owner/repo lookup keys.
In `@src/renderer/components/QuickLinkModal.tsx`:
- Around line 56-59: The quick-link string in handleCopyLink is using the wrong
format; replace `emdash.github.com/${owner}/${repo}` with the deep-link scheme
expected by DeepLinkServices (use `emdash-github://${owner}/${repo}`) so the
copied value resolves correctly. Update the template in QuickLinkModal.tsx
inside the handleCopyLink callback to build `emdash-github://${owner}/${repo}`
and keep the existing window.electronAPI.clipboardWriteText call and owner/repo
dependencies.
In `@src/renderer/hooks/useProjectManagement.tsx`:
- Around line 651-655: The deep-link handler registered via
window.electronAPI.onDeepLinkOpenProject currently looks up projects immediately
(using projects) and drops events if useQuery(['projects']) hasn't resolved;
instead, when the handler receives data.projectPath store that raw path in a
short-lived cache (e.g., pendingDeepLinkPath), then after the projects query
becomes ready (watch the query's isFetched/isSuccess from
useQuery(['projects'])), run the same path-normalization logic used elsewhere in
this hook to find and call activateProjectView(matchedProject) and clear the
pendingDeepLinkPath; ensure duplicate/late events are idempotent (ignore if
already activated) and keep unsubscribeOpenProject unchanged for cleanup.
---
Outside diff comments:
In `@src/main/main.ts`:
- Around line 148-156: Remove the duplicate second-instance registration and the
unconditional requestSingleInstanceLock call: ensure requestSingleInstanceLock()
is only invoked once at startup guarded by the existing isDev check (do not call
inside app.whenReady()), register app.on('second-instance', ...) and
app.on('open-url', ...) once at module-level, and capture any initial deep-link
URL from process.argv immediately on startup (before window creation) so it can
be queued and passed to handleDeepLinkUrl(url) after the main window is ready;
update the second-instance handler (the existing app.on('second-instance', ...)
and the open-url handler) to only restore/focus the BrowserWindow and call
handleDeepLinkUrl for the received URL, and remove the duplicate block and the
late open-url registration in app.whenReady so deep-links aren’t processed twice
or missed for the first instance.
---
Nitpick comments:
In `@src/main/preload.ts`:
- Around line 531-558: The ElectronAPI interface is missing the newly added
bridge methods/listeners (e.g., onDeepLinkClone, onDeepLinkOpenProject,
onDeepLinkProjectCloned, githubIsAuthenticated, githubGetStatus, githubGetUser,
githubGetRepositories, githubCloneRepository, githubQuickLinkClone); update the
exported ElectronAPI interface to include these exact members with matching
function signatures and return types (listener registration methods returning a
cleanup function, invoke methods returning appropriate Promise types or typed
results) so the preload bridge and type declaration are a single source of
truth—ensure the types for the listener payloads ({ owner, repo, repoUrl }, {
projectPath }) and github method parameters (repoUrl: string, localPath: string,
params: { owner: string; repo: string; repoUrl }) match the implementations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ed326386-49e8-4fe8-b125-3e09f4b9dc9b
📒 Files selected for processing (14)
infra/cloudflare-worker/index.tsinfra/cloudflare-worker/wrangler.tomlinfra/netlify-redirect/_redirectsinfra/netlify-redirect/index.htmlpackage.jsonsrc/main/ipc/githubIpc.tssrc/main/main.tssrc/main/preload.tssrc/main/services/DeepLinkServices.tssrc/renderer/components/QuickLinkModal.tsxsrc/renderer/components/QuickLinkModalOverlay.tsxsrc/renderer/contexts/ModalProvider.tsxsrc/renderer/hooks/useProjectManagement.tsxsrc/renderer/types/electron-api.d.ts
| @@ -0,0 +1 @@ | |||
| //:owner/:repo/* https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo 200! | |||
There was a problem hiding this comment.
Avoid shipping this through a personal workers.dev hostname.
Hardcoding emdash-github-redirect.yashdev-yvd.workers.dev makes the feature depend on a developer-owned Cloudflare subdomain. If that account, route, or deployment changes, every quick link breaks. Prefer an org-owned/custom domain or environment-specific host config.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/netlify-redirect/_redirects` at line 1, The redirect currently
hardcodes the personal Cloudflare workers host string
"emdash-github-redirect.yashdev-yvd.workers.dev" in the rule that begins
"//:owner/:repo/*"; replace this literal with an environment/config-driven host
(e.g., an org-owned/custom domain or a NETLIFY_REDIRECT_HOST variable) so the
redirect target is configurable per environment and not tied to a developer's
personal workers.dev subdomain. Ensure the redirect rule uses that config value
(or a build-time placeholder) instead of the hardcoded hostname.
🧩 Analysis chain
🌐 Web query:
Netlify _redirects file source pattern syntax double slash //
💡 Result:
In a Netlify _redirects file, the “from” (source) pattern is a URL path string (or a full URL for domain-level rules), written literally (no special meaning for //). Paths are case-sensitive, and “special characters” must be URL-encoded. [1]
However, Netlify’s built-in normalization/Pretty URLs guidance is about trailing slashes, and Netlify notes that it normalizes by removing a trailing slash (with exceptions like some internal rewrites). [2] It does not document a supported redirect-syntax feature to “collapse” multiple consecutive slashes in the middle/beginning of a path.
Practically, Netlify has known/observed behavior where requesting the site root with // or /// can return 200 (no redirect), while more slashes may redirect—so you shouldn’t rely on _redirects rules to canonicalize double-slash URLs. [3]
If you need to enforce canonical URLs (e.g., collapse // → /), use an Edge Function (or handle it in your app/origin) to normalize the path before routing/redirects.
Sources:
[1] Netlify Docs — Redirects and rewrites (syntax for _redirects) (docs.netlify.com)
[2] Netlify Docs — Redirect options (normalization / trailing slash / Pretty URLs notes) (docs.netlify.com)
[3] Netlify Support Forums — Multiple trailing slashes issue for site root (behavior with //, ///, etc.) (answers.netlify.com)
Citations:
- 1: https://docs.netlify.com/manage/routing/redirects/overview
- 2: https://docs.netlify.com/routing/redirects/redirect-options/
- 3: https://answers.netlify.com/t/multiple-trailing-slashes-issue-for-site-root/30675
Fix the source pattern; //:owner/:repo/* requires a literal double slash and won't match normal repo URLs.
The source pattern on line 1 is literal—it requires requests to start with //, so requests to /owner/repo will not match and the quick-link feature won't work.
Suggested fix
-//:owner/:repo/* https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo 200!
+/:owner/:repo/* https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo 200!📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //:owner/:repo/* https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo 200! | |
| /:owner/:repo/* https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo 200! |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/netlify-redirect/_redirects` at line 1, The redirect source pattern is
incorrectly using a literal double slash; replace the leading "//:owner/:repo/*"
with a single-leading-slash pattern "/:owner/:repo/*" so normal repo URLs like
"/owner/repo/..." match; keep the destination unchanged
(https://emdash-github-redirect.yashdev-yvd.workers.dev/:owner/:repo) and
preserve the 200! status.
| <script> | ||
| const path = window.location.pathname.replace(/^\//, '').split('/').filter(Boolean); | ||
| if (path.length >= 2) { | ||
| window.location.href = `emdash-github://${path[0]}/${path[1]}`; | ||
| } else { | ||
| document.body.innerHTML = '<h1>Emdash GitHub</h1><p>Use: /owner/repo</p>'; |
There was a problem hiding this comment.
The fallback page can run before <body> exists.
This script executes in <head>, so document.body can still be null when Line 10 runs. That turns the usage page into a blank/error state instead of showing the help text.
Suggested fix
- <script>
- const path = window.location.pathname.replace(/^\//, '').split('/').filter(Boolean);
- if (path.length >= 2) {
- window.location.href = `emdash-github://${path[0]}/${path[1]}`;
- } else {
- document.body.innerHTML = '<h1>Emdash GitHub</h1><p>Use: /owner/repo</p>';
- }
- </script>
+ <script>
+ window.addEventListener('DOMContentLoaded', () => {
+ const path = window.location.pathname.replace(/^\//, '').split('/').filter(Boolean);
+ if (path.length >= 2) {
+ window.location.replace(`emdash-github://${path[0]}/${path[1]}`);
+ } else {
+ document.body.innerHTML = '<h1>Emdash GitHub</h1><p>Use: /owner/repo</p>';
+ }
+ });
+ </script>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/netlify-redirect/index.html` around lines 5 - 10, The fallback writes
to document.body.innerHTML from a script that runs in head, which can be null;
modify the logic around the path check in the inline script so that if
path.length < 2 you wait for the DOM to be ready (e.g., wrap the fallback in a
DOMContentLoaded callback or check for document.body and defer until available)
before setting document.body.innerHTML, keeping the existing redirect behavior
(window.location.href = `emdash-github://${path[0]}/${path[1]}`) unchanged.
| const settings = getAppSettings(); | ||
| const projectDir = | ||
| settings.projects?.defaultDirectory || path.join(homedir(), 'emdash-projects'); | ||
| const localPath = path.join(projectDir, repo); | ||
|
|
||
| const cloneResult = await githubService.cloneRepository(repoUrl, localPath); |
There was a problem hiding this comment.
Scope the clone destination by owner to avoid repo-name collisions.
Using path.join(projectDir, repo) means ownerA/docs and ownerB/docs both resolve to the same directory. The second quick link can fail or end up pointing at the wrong checkout. Put quick-link clones under an owner-specific path before calling cloneRepository.
Suggested fix
const settings = getAppSettings();
const projectDir =
settings.projects?.defaultDirectory || path.join(homedir(), 'emdash-projects');
- const localPath = path.join(projectDir, repo);
+ const localPath = path.join(projectDir, owner, repo);
+ fs.mkdirSync(path.dirname(localPath), { recursive: true });
const cloneResult = await githubService.cloneRepository(repoUrl, localPath);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const settings = getAppSettings(); | |
| const projectDir = | |
| settings.projects?.defaultDirectory || path.join(homedir(), 'emdash-projects'); | |
| const localPath = path.join(projectDir, repo); | |
| const cloneResult = await githubService.cloneRepository(repoUrl, localPath); | |
| const settings = getAppSettings(); | |
| const projectDir = | |
| settings.projects?.defaultDirectory || path.join(homedir(), 'emdash-projects'); | |
| const localPath = path.join(projectDir, owner, repo); | |
| fs.mkdirSync(path.dirname(localPath), { recursive: true }); | |
| const cloneResult = await githubService.cloneRepository(repoUrl, localPath); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/ipc/githubIpc.ts` around lines 474 - 479, The clone destination is
currently just projectDir + repo which causes collisions across different
owners; change localPath to include the repo owner (e.g., projectDir/owner/repo)
before calling githubService.cloneRepository. Locate where getAppSettings(),
projectDir, localPath and githubService.cloneRepository are used and derive the
owner (from repoUrl or available repo metadata) and use path.join(projectDir,
owner, repo) for localPath so clones are scoped per owner.
| async findExistingProject(owner: string, repo: string): Promise<string | null> { | ||
| try { | ||
| const projects = await databaseService.getProjects(); | ||
| const githubUrl = this.buildGitHubUrl(owner, repo).toLowerCase(); | ||
| const project = projects.find((p) => { | ||
| const remote = (p.gitInfo as any)?.remote || ''; | ||
| return remote.toLowerCase().replace(/\.git$/, '') === githubUrl; | ||
| }); | ||
| return project?.path ?? null; |
There was a problem hiding this comment.
Normalize GitHub remotes before checking for an existing project.
This equality only matches https://github.com/owner/repo(.git). Any project cloned with git@github.com:owner/repo.git or ssh://git@github.com/owner/repo.git misses the lookup, so the quick link falls back to cloning even though the repo is already imported.
🧭 Proposed fix
- const githubUrl = this.buildGitHubUrl(owner, repo).toLowerCase();
+ const repoKey = `${owner}/${repo}`.toLowerCase();
const project = projects.find((p) => {
- const remote = (p.gitInfo as any)?.remote || '';
- return remote.toLowerCase().replace(/\.git$/, '') === githubUrl;
+ const remote = String((p.gitInfo as any)?.remote ?? '').trim();
+ const normalized = remote
+ .replace(/\.git$/i, '')
+ .replace(/^git@github\.com:/i, '')
+ .replace(/^ssh:\/\/git@github\.com\//i, '')
+ .replace(/^https:\/\/github\.com\//i, '')
+ .replace(/\/$/, '')
+ .toLowerCase();
+ return normalized === repoKey;
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/services/DeepLinkServices.ts` around lines 30 - 38, The
findExistingProject method only matches remotes that look like https URLs;
update it to normalize stored remotes before comparing to buildGitHubUrl(owner,
repo) by adding a helper (e.g., normalizeGitRemote) that: strips protocols
(https://, ssh://), strips "git@" and any userinfo, converts
"git@github.com:owner/repo(.git)" and "ssh://git@github.com/owner/repo(.git)"
into the canonical "https://github.com/owner/repo" form, removes trailing
".git", and lowercases the result; then use that normalized remote in the
projects.find comparison inside findExistingProject so all common Git remote
formats match the built GitHub URL.
| const settings = getAppSettings(); | ||
| const projectDir = | ||
| settings.projects?.defaultDirectory || path.join(app.getPath('home'), 'emdash-projects'); | ||
| const localPath = path.join(projectDir, repo); |
There was a problem hiding this comment.
Include the owner in the clone path.
localPath is currently <defaultDirectory>/<repo>, so acme/api and other/api both map to the same folder. Because the existing-project lookup keys on owner/repo, the second quick link collides with an unrelated checkout and clone fails.
🗂️ Proposed fix
- const localPath = path.join(projectDir, repo);
+ const localPath = path.join(projectDir, owner, repo);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/services/DeepLinkServices.ts` around lines 80 - 83, localPath
currently builds as path.join(projectDir, repo) which causes different owners
with the same repo name to collide; change the local path construction in
DeepLinkServices (where settings, projectDir and localPath are defined) to
include the repository owner (e.g., incorporate the owner variable into the path
such as path.join(projectDir, owner, repo) or a stable owner-repo filename like
`${owner}-${repo}`) so each checkout is unique and matches the owner/repo lookup
keys.
| const handleCopyLink = useCallback(() => { | ||
| const quickLink = `emdash.github.com/${owner}/${repo}`; | ||
| window.electronAPI.clipboardWriteText(quickLink); | ||
| }, [owner, repo]); |
There was a problem hiding this comment.
Copy the actual quick-link target here.
emdash.github.com/${owner}/${repo} matches neither the emdash-github://owner/repo parser in src/main/services/DeepLinkServices.ts nor the HTTPS redirect host described for this feature, so "Copy Quick Link" currently hands users a value the flow cannot resolve.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/QuickLinkModal.tsx` around lines 56 - 59, The
quick-link string in handleCopyLink is using the wrong format; replace
`emdash.github.com/${owner}/${repo}` with the deep-link scheme expected by
DeepLinkServices (use `emdash-github://${owner}/${repo}`) so the copied value
resolves correctly. Update the template in QuickLinkModal.tsx inside the
handleCopyLink callback to build `emdash-github://${owner}/${repo}` and keep the
existing window.electronAPI.clipboardWriteText call and owner/repo dependencies.
| const unsubscribeOpenProject = window.electronAPI.onDeepLinkOpenProject((data) => { | ||
| const project = projects.find((p) => p.path === data.projectPath); | ||
| if (project) { | ||
| activateProjectView(project); | ||
| } |
There was a problem hiding this comment.
Don’t drop deep-link:open-project before the project query has loaded.
projects is [] until useQuery(['projects']) resolves. On a cold start, an early open-project event hits Line 652, finds nothing, and is lost permanently. Cache the requested path until initial load completes, then resolve it with the same path-normalization logic used elsewhere in this hook.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/hooks/useProjectManagement.tsx` around lines 651 - 655, The
deep-link handler registered via window.electronAPI.onDeepLinkOpenProject
currently looks up projects immediately (using projects) and drops events if
useQuery(['projects']) hasn't resolved; instead, when the handler receives
data.projectPath store that raw path in a short-lived cache (e.g.,
pendingDeepLinkPath), then after the projects query becomes ready (watch the
query's isFetched/isSuccess from useQuery(['projects'])), run the same
path-normalization logic used elsewhere in this hook to find and call
activateProjectView(matchedProject) and clear the pendingDeepLinkPath; ensure
duplicate/late events are idempotent (ignore if already activated) and keep
unsubscribeOpenProject unchanged for cleanup.
Summary
Fixes
New feature - easily works on your own browser by replacing any github .com repo from 'github .com/owner/repo' -> 'emdash-github-redirect.yashdev-yvd.workers. dev/owner/repo'
and it will redirect and clone repo directly into Emdash.
Snapshot
Emdash-github-quicklink.mp4
Type of change
Mandatory Tasks
Checklist
pnpm run format)pnpm run lint)Summary by CodeRabbit
Release Notes
emdash-github://owner/repo)