Skip to content
Open
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
42 changes: 39 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"build:vscode": "bun run --cwd apps/vscode-extension build",
"package:vscode": "bun run --cwd apps/vscode-extension package",
"test": "bun test",
"typecheck": "bash apps/pi-extension/vendor.sh && tsc --noEmit -p packages/shared/tsconfig.json && tsc --noEmit -p packages/ai/tsconfig.json && tsc --noEmit -p packages/server/tsconfig.json && tsc --noEmit -p apps/pi-extension/tsconfig.json"
"typecheck": "bash apps/pi-extension/vendor.sh && tsc --noEmit -p packages/shared/tsconfig.json && tsc --noEmit -p packages/ai/tsconfig.json && tsc --noEmit -p packages/server/tsconfig.json && tsc --noEmit -p packages/ui/tsconfig.json && tsc --noEmit -p apps/pi-extension/tsconfig.json"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.92",
Expand Down
6 changes: 3 additions & 3 deletions packages/editor/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const App: React.FC = () => {
const [sharingEnabled, setSharingEnabled] = useState(true);
const [shareBaseUrl, setShareBaseUrl] = useState<string | undefined>(undefined);
const [pasteApiUrl, setPasteApiUrl] = useState<string | undefined>(undefined);
const [repoInfo, setRepoInfo] = useState<{ display: string; branch?: string } | null>(null);
const [repoInfo, setRepoInfo] = useState<{ display: string; branch?: string; host?: string } | null>(null);
const [projectRoot, setProjectRoot] = useState<string | null>(null);
const [wideModeType, setWideModeType] = useState<WideModeType | null>(null);
const wideModeSnapshotRef = useRef<WideModeLayoutSnapshot | null>(null);
Expand Down Expand Up @@ -642,7 +642,7 @@ const App: React.FC = () => {
if (!res.ok) throw new Error('Not in API mode');
return res.json();
})
.then((data: { plan: string; origin?: Origin; mode?: 'annotate' | 'annotate-last' | 'annotate-folder' | 'archive'; filePath?: string; sourceInfo?: string; sharingEnabled?: boolean; shareBaseUrl?: string; pasteApiUrl?: string; repoInfo?: { display: string; branch?: string }; previousPlan?: string | null; versionInfo?: { version: number; totalVersions: number; project: string }; archivePlans?: ArchivedPlan[]; projectRoot?: string; isWSL?: boolean; serverConfig?: { displayName?: string; gitUser?: string } }) => {
.then((data: { plan: string; origin?: Origin; mode?: 'annotate' | 'annotate-last' | 'annotate-folder' | 'archive'; filePath?: string; sourceInfo?: string; sharingEnabled?: boolean; shareBaseUrl?: string; pasteApiUrl?: string; repoInfo?: { display: string; branch?: string; host?: string }; previousPlan?: string | null; versionInfo?: { version: number; totalVersions: number; project: string }; archivePlans?: ArchivedPlan[]; projectRoot?: string; isWSL?: boolean; serverConfig?: { displayName?: string; gitUser?: string } }) => {
// Initialize config store with server-provided values (config file > cookie > default)
configStore.init(data.serverConfig);
// gitUser drives the "Use git name" button in Settings; stays undefined (button hidden) when unavailable
Expand Down Expand Up @@ -1365,7 +1365,7 @@ const App: React.FC = () => {
<TooltipProvider delayDuration={900} skipDelayDuration={200} disableHoverableContent>
<div data-print-region="root" className="h-screen flex flex-col bg-background overflow-hidden">
{/* Minimal Header */}
<header className="h-12 flex items-center justify-between px-2 md:px-4 border-b border-border/50 bg-card/50 backdrop-blur-xl sticky top-0 z-[50]">
<header data-app-header="true" className="h-12 flex items-center justify-between px-2 md:px-4 border-b border-border/50 bg-card/50 backdrop-blur-xl sticky top-0 z-[50]">
<div className="flex items-center gap-2 md:gap-3">
<a
href="https://plannotator.ai"
Expand Down
8 changes: 5 additions & 3 deletions packages/server/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { $ } from "bun";

import type { RepoInfo } from "@plannotator/shared/repo";
import { parseRemoteUrl, getDirName } from "@plannotator/shared/repo";
import { parseRemoteUrl, parseRemoteHost, getDirName } from "@plannotator/shared/repo";

/**
* Get current git branch
Expand Down Expand Up @@ -40,10 +40,12 @@ export async function getRepoInfo(): Promise<RepoInfo | null> {
try {
const result = await $`git remote get-url origin`.quiet().nothrow();
if (result.exitCode === 0) {
const orgRepo = parseRemoteUrl(result.stdout.toString().trim());
const remoteUrl = result.stdout.toString().trim();
const orgRepo = parseRemoteUrl(remoteUrl);
if (orgRepo) {
branch = await getCurrentBranch();
return { display: orgRepo, branch };
const host = parseRemoteHost(remoteUrl) ?? undefined;
return { display: orgRepo, branch, host };
}
}
} catch {
Expand Down
25 changes: 25 additions & 0 deletions packages/shared/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ export interface RepoInfo {
display: string;
/** Current git branch (if in a git repo) */
branch?: string;
/** Host of the git remote (e.g., "github.com", "gitlab.com"). Populated */
/** only when the remote URL is parseable; absent for directory-only fallbacks. */
host?: string;
}

/**
Expand Down Expand Up @@ -35,6 +38,28 @@ export function parseRemoteUrl(url: string): string | null {
return null;
}

/**
* Parse the host from a git remote URL. Returns null when the shape
* doesn't match a known remote form. Used to identify the forge
* (github.com, gitlab.com, self-hosted) so inline mention / issue
* refs can link to the correct destination instead of assuming GitHub.
*/
export function parseRemoteHost(url: string): string | null {
if (!url) return null;
// ssh://git@host:port/path
const sshPort = url.match(/^ssh:\/\/(?:[^@]+@)?([^:/]+)/i);
if (sshPort) return sshPort[1];
// git@host:path
if (!url.includes('://')) {
const ssh = url.match(/^[^@\s]+@([^:\s]+):/);
if (ssh) return ssh[1];
}
// https://host/path or http://host/path
const https = url.match(/^https?:\/\/([^/:]+)/i);
if (https) return https[1];
return null;
}

/**
* Get directory name from path
*/
Expand Down
1 change: 1 addition & 0 deletions packages/ui/components/AnnotationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const AnnotationPanel: React.FC<PanelProps> = ({

const panel = (
<aside
data-annotation-panel="true"
className={`border-l border-border/50 bg-card/30 backdrop-blur-sm flex flex-col flex-shrink-0 ${
isMobile ? 'fixed top-12 bottom-0 right-0 z-[60] w-full max-w-sm shadow-2xl bg-card' : ''
}`}
Expand Down
Loading
Loading