summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-15 03:37:44 +0000
committerGitHub <noreply@github.com>2026-01-15 03:37:44 +0000
commit764bd28d08ceaef03cd4050f9568a62d77bbcfca (patch)
treedbd83ea7d213902f2b8021acc98798b6f3545946 /makima/frontend/src/components
parenteeafe072bc6bb81459f7d087b48fc921afe9cc11 (diff)
downloadsoryu-764bd28d08ceaef03cd4050f9568a62d77bbcfca.tar.gz
soryu-764bd28d08ceaef03cd4050f9568a62d77bbcfca.zip
Add repository history feature to store and suggest previously used repositories (#18)
- Add repository_history table migration with repo_type, repo_path, use_count, last_used_at - Add RepositoryHistoryEntry model and CRUD database functions - Create API endpoints: GET/POST/DELETE /api/v1/repository-history, GET /api/v1/repository-history/suggestions - Update add_remote_repository and add_local_repository handlers to automatically track history - Update frontend API with repository history types and functions - Add Repository History section to Settings page with list of entries and delete functionality - Add suggestions dropdown to RepositoryPanel when entering new repository URL/path - Suggestions filter by repo type (remote vs local) and match on user input Test plan: - Add a remote repository to a contract - verify it appears in Settings history - Add a local repository to a contract - verify it appears in Settings history - Add same repository again - verify use_count increments, not duplicate - When adding new repository, verify suggestions appear based on history - Delete a history entry from Settings - verify it's removed - Verify suggestions only show matching type (remote for remote, local for local) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/components')
-rw-r--r--makima/frontend/src/components/contracts/RepositoryPanel.tsx69
1 files changed, 67 insertions, 2 deletions
diff --git a/makima/frontend/src/components/contracts/RepositoryPanel.tsx b/makima/frontend/src/components/contracts/RepositoryPanel.tsx
index 4170cfb..e314140 100644
--- a/makima/frontend/src/components/contracts/RepositoryPanel.tsx
+++ b/makima/frontend/src/components/contracts/RepositoryPanel.tsx
@@ -4,8 +4,9 @@ import type {
RepositorySourceType,
RepositoryStatus,
DaemonDirectory,
+ RepositoryHistoryEntry,
} from "../../lib/api";
-import { getDaemonDirectories } from "../../lib/api";
+import { getDaemonDirectories, getRepositorySuggestions } from "../../lib/api";
import { DirectoryInput } from "../mesh/DirectoryInput";
interface RepositoryPanelProps {
@@ -53,6 +54,9 @@ export function RepositoryPanel({
const [isPrimary, setIsPrimary] = useState(false);
// Daemon directory suggestions for local repositories
const [suggestedDirectories, setSuggestedDirectories] = useState<DaemonDirectory[]>([]);
+ // Repository history suggestions
+ const [repoSuggestions, setRepoSuggestions] = useState<RepositoryHistoryEntry[]>([]);
+ const [showSuggestions, setShowSuggestions] = useState(false);
// Fetch daemon directories when "local" mode is selected
useEffect(() => {
@@ -63,6 +67,36 @@ export function RepositoryPanel({
}
}, [addMode]);
+ // Fetch repository suggestions when mode changes to remote or local
+ useEffect(() => {
+ if (addMode === "remote" || addMode === "local") {
+ getRepositorySuggestions(addMode, undefined, 10)
+ .then((res) => {
+ setRepoSuggestions(res.entries);
+ setShowSuggestions(res.entries.length > 0);
+ })
+ .catch(() => {
+ setRepoSuggestions([]);
+ setShowSuggestions(false);
+ });
+ } else {
+ setRepoSuggestions([]);
+ setShowSuggestions(false);
+ }
+ }, [addMode]);
+
+ // Apply a suggestion to the form
+ const applySuggestion = (suggestion: RepositoryHistoryEntry) => {
+ setName(suggestion.name);
+ if (suggestion.repositoryUrl) {
+ setUrl(suggestion.repositoryUrl);
+ }
+ if (suggestion.localPath) {
+ setPath(suggestion.localPath);
+ }
+ setShowSuggestions(false);
+ };
+
const handleAdd = () => {
if (!name.trim()) return;
@@ -162,12 +196,43 @@ export function RepositoryPanel({
{/* Add repository form */}
{addMode ? (
<div className="p-3 border border-[rgba(117,170,252,0.3)] space-y-3">
- <div className="flex items-center gap-2 mb-2">
+ <div className="flex items-center justify-between mb-2">
<span className="font-mono text-xs text-[#75aafc] uppercase">
Add {sourceTypeLabels[addMode]} Repository
</span>
+ {repoSuggestions.length > 0 && (
+ <button
+ onClick={() => setShowSuggestions(!showSuggestions)}
+ className="font-mono text-[10px] text-[#555] hover:text-[#9bc3ff] transition-colors"
+ >
+ {showSuggestions ? "Hide suggestions" : `${repoSuggestions.length} suggestions`}
+ </button>
+ )}
</div>
+ {/* Suggestions dropdown */}
+ {showSuggestions && repoSuggestions.length > 0 && (
+ <div className="border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-32 overflow-y-auto">
+ {repoSuggestions.map((suggestion) => (
+ <button
+ key={suggestion.id}
+ onClick={() => applySuggestion(suggestion)}
+ className="w-full text-left px-3 py-2 font-mono text-xs hover:bg-[rgba(117,170,252,0.1)] transition-colors border-b border-[rgba(117,170,252,0.1)] last:border-b-0"
+ >
+ <div className="flex items-center justify-between">
+ <span className="text-[#9bc3ff] truncate">{suggestion.name}</span>
+ <span className="text-[10px] text-[#556677]">
+ {suggestion.useCount}×
+ </span>
+ </div>
+ <div className="text-[10px] text-[#556677] truncate">
+ {suggestion.repositoryUrl || suggestion.localPath}
+ </div>
+ </button>
+ ))}
+ </div>
+ )}
+
<input
type="text"
value={name}