Files
arboretum/packages/web/src/components/ide/ProjectTree.vue
Johan LEROY 063f5e928b feat(web): refonte visuelle « Emerald » (thème clair/sombre, polices, tokens)
Adopte le langage visuel du design system Emerald dans l'IDE web :
neutres zinc + accent emerald, polices auto-hébergées Inter + JetBrains
Mono, radii/pills mono, points de statut, motif terminal, halo/grain.
Refonte purement visuelle et additive (layout, routing, stores, protocole
inchangés).

- style.css : défauts sombres dans @theme + override clair sous
  html[data-theme=light] ; accent scindé (bright #34d399 / solid #059669 /
  hover) ; tokens border-soft/info/warn/danger/coffee ; idiomes globaux
  (focus/selection accent, scrollbars fines, jl-pulse/jl-blink, classes
  .chip/.status/.eyebrow/.label-mono/.caret/.halo/.grain, .btn*/.badge pill).
- lib/theme.ts : singleton themeMode/resolvedTheme (dark/light/system,
  persistedRef arb.theme) + application dataset.theme/metas ; script
  anti-FOUC inline dans index.html ; toggle ActivityBar + Réglages.
- Thèmes Monaco + xterm dark/light réactifs (palette ANSI + coloration
  syntaxique) ; fonts.ready + remeasureFonts pour l'alignement des glyphes.
- status-tokens : tons sémantiques tokenisés ; balayage des ~340 couleurs
  Tailwind brutes vers les tokens sur tout components/* et views/*
  (exceptions assumées commentées).
- GroupSummary.color exploité : icône de groupe teintée + ColorSwatchPicker
  (lib/group-colors.ts) dans les modals de groupe.
- Polices via @fontsource-variable/{inter,jetbrains-mono} (offline).
2026-07-20 21:30:57 +02:00

83 lines
2.7 KiB
Vue

<template>
<div class="flex h-full min-h-0 flex-col">
<div class="label-mono flex items-center gap-1 px-3 py-2">
{{ t('ide.projects') }}
<button
type="button"
class="ml-auto rounded p-0.5 hover:bg-surface-2 hover:text-fg"
:title="t('repos.scan')"
:disabled="scanning"
@click="onScan"
>
<ScanSearch :size="14" />
</button>
<button type="button" class="rounded p-0.5 hover:bg-surface-2 hover:text-fg" :title="t('ide.addProject')" @click="openAddMenu">
<Plus :size="14" />
</button>
</div>
<div class="min-h-0 flex-1 overflow-auto pb-2">
<p v-if="repos.length === 0" class="px-3 py-2 text-xs text-fg-subtle">{{ t('ide.noProjects') }}</p>
<ProjectTreeNode v-for="repo in repos" :key="repo.id" :repo="repo" />
</div>
<DirectoryPicker v-if="showPicker" mode="repo" :initial-path="''" @select="onPick" @close="showPicker = false" />
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { CloudDownload, FolderPlus, Plus, ScanSearch } from '@lucide/vue';
import { useWorktreesStore } from '../../stores/worktrees';
import { useToastsStore } from '../../stores/toasts';
import { useModalsStore } from '../../stores/modals';
import { useContextMenu } from '../../composables/useContextMenu';
import ProjectTreeNode from './ProjectTreeNode.vue';
import DirectoryPicker from '../DirectoryPicker.vue';
import CloneRepoModal from '../CloneRepoModal.vue';
const { t } = useI18n();
const worktrees = useWorktreesStore();
const toasts = useToastsStore();
const modals = useModalsStore();
const ctx = useContextMenu();
const showPicker = ref(false);
const scanning = ref(false);
// « + » : ajouter un dépôt local (DirectoryPicker) ou cloner un dépôt distant (CloneRepoModal).
function openAddMenu(e: MouseEvent): void {
ctx.open(e.clientX, e.clientY, [
{ label: t('ide.menu.addLocal'), icon: FolderPlus, onSelect: () => { showPicker.value = true; } },
{ label: t('ide.menu.cloneRemote'), icon: CloudDownload, onSelect: () => void modals.open(CloneRepoModal) },
]);
}
const repos = computed(() =>
[...worktrees.repos].filter((r) => !r.hidden).sort((a, b) => a.label.localeCompare(b.label)),
);
async function onScan(): Promise<void> {
scanning.value = true;
try {
const res = await worktrees.discover();
toasts.success(t('repos.scanResult', res.added));
} catch (err) {
toasts.error(err);
} finally {
scanning.value = false;
}
}
async function onPick(path: string): Promise<void> {
showPicker.value = false;
try {
await worktrees.addRepo(path);
toasts.success(t('toast.repoAdded'));
} catch (err) {
toasts.error(err);
}
}
</script>