TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Une liste de courses partagée en temps réel, à héberger entièrement sur Cloudflare (Workers + Durable Objects, sans base de données externe).README 전문 보기 ↗
Une liste de courses partagée en temps réel, à héberger entièrement sur Cloudflare (Workers + Durable Objects, sans base de données externe).
/l/CODE), avec le
partage natif du téléphone quand il est disponible.2 kg pommes ou yaourts x8, l'app détecte la quantité et l'affiche dans
un badge séparé, modifiable en un clic.Le seul contrôle d'accès à une liste est son code à 6 caractères (32^6, soit ~1 milliard de combinaisons) : il n'y a ni compte ni mot de passe. Toute personne qui obtient le code peut voir et modifier la liste — un rappel affiché sur l'écran d'accueil et dans la liste le précise aux utilisateurs. Aucune limitation de débit n'est en place sur la création ou la lecture d'une liste par code : c'est un choix assumé pour rester un projet personnel simple à héberger, pas un compromis invisible.
Les données de toute liste sont chiffrées au repos côté serveur (AES-GCM,
clé dérivée du code de la liste — voir worker/crypto.ts) : ça protège
contre un accès direct au stockage brut du Durable Object sans connaître le
code, mais pas contre quelqu'un qui a déjà le code/lien de partage, qui peut
ouvrir la liste normalement. À noter : la clé n'a pas plus d'entropie que le
code lui-même (~30 bits) — ça protège d'une lecture accidentelle du
stockage brut, pas d'un adversaire motivé qui l'obtiendrait et le
casserait hors-ligne. Les listes créées avant l'introduction de ce
chiffrement sont migrées automatiquement (sans étape manuelle) : elles sont
stockées en clair jusqu'à leur prochaine écriture, qui les rechiffre.
Les champs reçus du client (WebSocket ou fichier importé) sont revalidés
côté serveur avant stockage/diffusion (worker/reducer.ts) : un identifiant,
une priorité ou une couleur de catégorie mal formés sont corrigés ou ignorés
plutôt que stockés tels quels, et les noms/quantités sont plafonnés en
longueur.
Les photos d'articles sont stockées non chiffrées dans un bucket R2 séparé
(voir worker/index.ts) : même modèle de confiance que le reste (accessible
à qui obtient le code de la liste), plafonnées en taille et en type de
fichier accepté côté serveur (shared/photo.ts), jamais réutilisables d'une
liste à l'autre (id imprévisible, préfixé par le code — voir
worker/photos.ts).
@cloudflare/vite-plugin
pour un dev loop unique (front + Worker tournent dans le même processus,
avec workerd) + vite-plugin-pwa
pour le manifest/service worker.qrcode pour générer les QR codes
côté client.npm install
npm run dev
Ouvre l'URL affichée (http://localhost:5173 par défaut). Le plugin
Cloudflare fait tourner le Worker et le Durable Object localement.
npx wrangler r2 bucket create koikimanke-photos # une seule fois, avant le premier déploiement
npm run deploy
Ceci build le front (vite build, qui produit aussi une config Wrangler
prête à l'emploi dans dist/) puis déploie avec wrangler deploy. Il faut
être connecté à un compte Cloudflare (npx wrangler login la première
fois). Le bucket R2 (photos d'articles) doit exister avant le premier
déploiement : wrangler.json référence son nom mais ne le crée pas
lui-même. En local (npm run dev), le plugin Cloudflare le simule
automatiquement, sans compte ni bucket réel.
L'app est aussi accessible via une URL github.io, en plus de l'URL
Cloudflare Workers : le client est alors servi par GitHub Pages mais parle
toujours à l'unique Worker Cloudflare (API + WebSocket temps réel) via CORS,
donc les deux URLs donnent accès aux mêmes listes partagées.
C'est géré par .github/workflows/pages.yml, qui build le client avec
VITE_SYNC_WORKER_URL pointant vers l'URL publique du Worker (variable de
dépôt vars.DEPLOY_URL, la même que celle utilisée par deploy.yml) puis
publie dist/client sur GitHub Pages. Rien à faire manuellement une fois
Pages activé une première fois dans Settings → Pages (Source: "GitHub
Actions") ; VITE_SYNC_WORKER_URL absente (déploiement Cloudflare seul) =
chemins /api/... relatifs, comportement inchangé.
worker/ Worker Cloudflare (routes API), Durable Object ListRoom,
et reducer.ts (logique pure, testée unitairement)
shared/ Types et logique partagés entre le Worker et le client
(parsing de quantité inclus)
src/ Application front (vue Accueil / vue Liste, composants,
utilitaires : websocket, drag & drop, stockage local…)
e2e/ Tests fonctionnels Playwright (parcours principal, sync
temps réel multi-appareils)
wrangler.json Configuration Cloudflare (Durable Object, assets SPA)
npm run lint # oxlint
npm run typecheck
npm run test:coverage # Vitest — tout le dépôt (shared/, worker/, src/),
# 100% de couverture
npm run test:mutation # Stryker — mutation testing sur la seule logique pure
# (shared/, worker/, color.ts/sort.ts)
npm run test:e2e # Playwright, contre `vite dev`
worker/listRoom.ts (la fine couche Durable Object : stockage, hibernation
WebSocket) n'est volontairement pas couvert par les tests unitaires — toute
sa logique métier vit dans worker/reducer.ts, entièrement testé ; le
comportement de listRoom.ts lui-même est vérifié par les tests e2e contre
une vraie instance vite dev (Worker + Durable Object réels via workerd).
.github/workflows/ : ci.yml (lint, typecheck, tests + couverture,
mutation testing, e2e, audit, build) et deploy.yml (déploiement Cloudflare gaté sur la réussite
de la CI via workflow_run, jamais sur un simple push direct ; vérification
post-déploiement ; tag deploy-N à chaque déploiement réussi pour pouvoir
identifier/revenir à une version). Dependabot et CodeQL sont aussi
configurés.
Chaque liste est un unique objet JSON stocké dans son Durable Object :
articles, catégories, historique des noms déjà utilisés. Les mutations
(ajout, coche, déplacement, catégories…) sont envoyées en WebSocket sous
forme de petits messages typés (shared/types.ts), appliquées côté serveur,
persistées puis rediffusées à tous les clients connectés.
La photo d'un article fait exception : elle vit dans un bucket R2 séparé, un
article ne gardant qu'un identifiant (Item.photoId) vers elle — jamais ses
octets dans le ListState lui-même, qui est rediffusé en entier à chaque
mutation (voir ci-dessus) et mis en cache tel quel côté client
(localStorage). L'intégrer directement y aurait gonflé chaque diffusion et
chaque cache, même pour un appareil qui ne regarde jamais cette photo.