SPB Git

spb/prisme Public MIT

Navigateur iOS intelligent — chaque page comprise localement avant d'être affichée. SwiftUI · WebKit · Foundation Models, 100% on-device.

Swift 96.7% JavaScript 3.3%
ZIP
NameLast commitUpdated
Design feat: initial commit 2 h ago
docs feat: initial commit 2 h ago
Prisme feat: initial commit 2 h ago
PrismeUITests feat: initial commit 2 h ago
.gitignore build: TestFlight distribution config 1 h ago
CLAUDE.md feat: initial commit 2 h ago
LICENSE feat: initial commit 2 h ago
project.yml build: TestFlight distribution config 1 h ago
README.md feat: initial commit 2 h ago
README.md
Icône Prisme

# Prisme

Un navigateur n'est pas un afficheur de pages. C'est un lecteur qui comprend ce qu'il affiche.

Plateforme Swift UI IA Tests Dépendances Concurrence Licence

SwiftUI · WebKit · Foundation Models · SwiftData · NaturalLanguage — zéro serveur, zéro compte, zéro télémétrie.


# Table des matières


# 🔭 Vision

Le web moderne est hostile : bannières, murs de consentement, 2 000 mots de remplissage SEO pour une réponse de 40 mots, patterns manipulateurs, pistage. Les navigateurs actuels rendent fidèlement cette hostilité. Prisme s'interpose : chaque page est comprise localement avant d'être affichée, puis re-présentée selon l'intention de l'utilisateur.

Trois règles non négociables :

# Règle Concrètement
1 Rien ne quitte l'appareil par défaut Distillation, embeddings, historique, digest : tout est calculé on-device. Aucun serveur opéré, aucun compte.
2 Utile dès la première session, pour un seul utilisateur Aucun effet de réseau requis. Le bandeau de compréhension démontre la valeur en 10 secondes.
3 Le modèle ne remplace jamais la page Contenu généré toujours distinct visuellement (violet ✨), toujours ancré vers sa source DOM, page brute toujours à un geste.

# 📱 Captures d'écran

Page d'accueil Barre à intention Bandeau de compréhension
Page d'accueil avec logo prisme et univers Propositions : adresse directe, Google, DuckDuckGo Bandeau : temps de lecture et bouton Lire
Univers isolés, logo dessiné en code URL, moteurs au choix, moteur par défaut « ~1 min de lecture » + entrée lecteur
Lecteur — Texte Lecteur — Essentiel Rappel d'historique Bibliothèque
Rendu natif des blocs de la page Niveau essentiel : une phrase Page retrouvée en tapant dans la barre Favori structuré en données natives
Rendu natif typé Zoom sémantique max « Déjà visité » Données natives

# ✨ Fonctionnalités

Légende : ✅ implémenté · 🚧 prévu (P1) · 🔮 plus tard (P2)

# Affichage

État Fonctionnalité Description
Zoom sémantique Le pincement ne change pas la taille du texte : il change le niveau de détail. 4 niveaux — Texte · Sections · Plan · Essentiel — et écarter au-delà du texte ramène la page brute. La fonctionnalité signature.
Rendu adaptatif par type Le kind du digest sélectionne la typographie : un article passe en serif de lecture, une documentation garde son code monospace défilant.
Bandeau de compréhension Après chaque chargement : « Article · ~4 min de lecture » + l'essentiel généré. Un tap → lecteur. Jamais un popup.
🚧 Barre de défilement sémantique La scrollbar devient une carte de la page.
🚧 Thème sémantique Le mode sombre par rôle de bloc, pas par inversion.
🚧 Tiroir du bruit Tout ce qui a été retiré, consultable.
🔮 Diff temporel Ce qui a changé depuis la dernière visite, surligné. (Les snapshots texte sont déjà archivés à chaque visite.)

# Onglets & entrée

État Fonctionnalité Description
Barre à intention Un champ unique qui propose — jamais ne devine en silence : adresse directe, recherche DuckDuckGo, recherche Google, pages déjà visitées. Moteur par défaut commutable dans la carte même.
Pool de WKWebView Jamais une webview par onglet : pool réutilisé, état de session (interactionState) préservé au changement d'onglet — historique arrière/avant et défilement intacts.
Sélecteur d'onglets par univers Cartes sectionnées par conteneur, bandeau de couleur, badge de site.
🚧 Regroupement par intention Proposé, jamais imposé.
🚧 Reprise narrative À la réouverture : un paragraphe, pas 47 vignettes.
🚧 Onglets périssables Durée de vie estimée, purge proposée.

# Favoris repensés

État Fonctionnalité Description
L'extrait Sélectionner un passage → « Sauver l'extrait » dans le menu d'édition natif. Gardé : le texte, la source, la date, le contexte de section, l'ancre DOM.
Le favori structuré La page sauvée en données natives : titre, essentiel, plan, type — pas un pointeur qui pourrit. Enrichi par le modèle quand disponible (marqué ✨).
🚧 Le favori vivant Surveille sa page en tâche de fond (BGTaskScheduler), notifie au changement.
🚧 Le favori-question « Combien coûte le passeport » plutôt qu'une URL — immunisé contre le lien mort.
🔮 Collections émergentes · Purge honnête Suggestions, jamais d'action automatique.

# Mémoire

État Fonctionnalité Description
Historique sémantique Chaque page visitée est distillée et indexée localement (embeddings de phrases NaturalLanguage, par langue). Recherche en langage naturel, rappel directement dans la barre à intention (« Déjà visité »).
Snapshots texte Le texte extrait de chaque visite est archivé — la matière première du futur diff temporel.
🚧 Ligne du temps de sujet Toutes les visites autour d'un thème.
🚧 Rappel proactif « Vu en mars, c'était 899 $ » — une ligne, jamais un popup.

# Vie privée

État Fonctionnalité Description
Conteneurs d'identité Perso · Travail · Magasinage · Recherche sensible — cookies, sessions et empreinte cloisonnés par WKWebsiteDataStore(forIdentifier:). Changement d'univers en un geste.
Blocage de contenu WKContentRuleList compilée (14 règles anti-traqueurs), appliquée à chaud dès compilation, hors du chemin critique de rendu.
Exclusion du sensible Les visites de l'univers « Recherche sensible » ne sont jamais écrites dans l'historique — pas stockées-puis-filtrées : jamais vues.
🚧 Détecteur de patterns manipulateurs Faux compte à rebours, consentement pré-coché : nommés à l'écran.
🚧 Traducteur de conditions Les CGU en trois lignes avant d'accepter.

# 🧠 Intelligence locale

Un LLM n'est pas une réponse à tout — c'est le dernier recours, pas le premier. Chaque tâche déclare son niveau ; le routeur décide. Aucun appel direct au modèle ailleurs.

Tier Quand Exemples dans Prisme
none Une heuristique suffit Détection URL/recherche, extraction DOM, temps de lecture, plan par titres, résumés de repli (première phrase), embeddings de recherche
local Fréquent, gratuit, illimité, hors ligne Digest de page (type, essentiel, plan, affirmations chiffrées) via SystemLanguageModel
cloud Action explicite uniquement — pas encore câblé Comparaisons multi-onglets, synthèses longues (Private Cloud Compute)

# Le Distiller — pièce centrale

Une page fait 30 000 tokens ; le modèle en accepte 4–8 000. Pipeline en 5 étapes, déterministe sauf la dernière :

text
HTML ──▶ ① Extraction DOM (extractor.js, injecté à documentEnd, zéro IA)
     ──▶ ② Blocs typés (titre, paragraphe, code, tableau…) + chemins DOM
     ──▶ ③ Budgétisation (contextSize / tokenCount, 30 % réservés à la réponse)
     ──▶ ④ Condensation déterministe (jamais de coupe en pleine phrase)
     ──▶ ⑤ Génération guidée (@Generable PageDigest — jamais de texte libre à parser)
  • Cache par hash de contenu : une même page n'est jamais distillée deux fois.
  • File d'inférence : max 2 requêtes en vol, priorité geste > page active > arrière-plan.
  • Honnêteté structurelle : chaque élément généré porte l'index de son bloc source ; un résumé sans ancre ne s'affiche pas ; les refus du modèle sont des états calmes, jamais des erreurs système brutes.
  • Dégradation totale : sur un appareil sans Foundation Models (< A17 Pro), tout fonctionne — lecteur, zoom, historique, favoris — via les chemins déterministes. La suite de tests tourne avec le modèle indisponible.

# 🏛 Architecture

text
Prisme/
├─ App/                    point d'entrée, modèle racine
├─ Browser/
│  ├─ Engine/              WKWebView (pool, proxy, délégués, règles) — seul accès WebKit
│  ├─ Tabs/                onglets, insight de page
│  ├─ Chrome/              barre à intention, bandeau, toolbar, sélecteur
│  └─ Reader/              zoom sémantique (4 niveaux, pincement)
├─ Intelligence/
│  ├─ Router/              choix du tier + disponibilité du modèle
│  ├─ Distiller/           extractor.js, blocs, budgets, cache        ⚠️ cœur du projet
│  ├─ Schemas/             types @Generable (PageDigest…)
│  └─ Sessions/            file d'inférence à priorité
├─ Memory/
│  ├─ Index/               embeddings de phrases (NaturalLanguage)
│  ├─ Snapshots/           visites SwiftData + textes archivés
│  └─ Recall/              recherche naturelle, vue historique
├─ Library/                extraits + favoris structurés (SwiftData)
├─ Privacy/                conteneurs d'identité
└─ Design/                 tokens, logo PrismMark (Canvas)

Règles d'or du code :

  • WKWebView enveloppée une seule fois (Browser/Engine) ; le reste de l'app parle à WebPageProxy.
  • Jamais de String libre en sortie de modèle — toujours @Generable.
  • Le JS injecté vit dans des fichiers .js versionnés, jamais dans des chaînes Swift.
  • Chaque store SwiftData a son fichier (history.store, library.store).
  • Chaque fichier commence par l'en-tête d'auteur.

# 📊 Métriques

Métrique Valeur
Fichiers Swift 35
Lignes Swift ~4 100
JavaScript injecté 144 lignes (extraction + sélection + scroll-to-source)
Dépendances tierces 0 — uniquement les frameworks Apple
Tests UI de bout en bout 6/6 ✓
Cible de déploiement iOS 26.4+ (requis par tokenCount(for:))
Concurrence Swift 6, SWIFT_STRICT_CONCURRENCY = complete
Règles de blocage 14 familles de traqueurs (troisième partie)

Répartition par module (fichiers Swift) :

Module Fichiers Rôle
Browser/ 14 moteur, onglets, chrome, lecteur
Intelligence/ 7 routeur, distiller, schémas, sessions
Memory/ 4 index, snapshots, rappel
Library/ 3 extraits, favoris structurés
App/ · Design/ · Privacy/ 6 racine, tokens, conteneurs
PrismeUITests/ 1 6 tests E2E

# 🛠 Compiler et lancer

Prérequis : Xcode 26.6+ (SDK iOS 26.4+), XcodeGen.

bash
git clone https://git.spboucher.ai/prisme.git
cd prisme
xcodegen generate                  # génère Prisme.xcodeproj depuis project.yml
open Prisme.xcodeproj              # ⌘R sur un simulateur iPhone

En ligne de commande :

bash
xcodebuild -project Prisme.xcodeproj -scheme Prisme \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  build CODE_SIGNING_ALLOWED=NO

L'icône se régénère depuis le SVG source :

bash
rsvg-convert -w 1024 -h 1024 Design/icon/prisme-icon.svg \
  -o Prisme/App/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png

Note simulateur : SystemLanguageModel s'y déclare disponible mais échoue à l'inférence (assets absents). Les chemins déterministes prennent le relais — c'est le comportement attendu. Pour les digests réels : iPhone A17 Pro+ avec Apple Intelligence activée.


# ✅ Tests

Six tests XCUITest de bout en bout — réseau réel, gestes réels, zéro mock :

Test Ce qu'il prouve
testNavigateToURLLoadsPage Saisie → proposition « Aller sur » → la page charge, cadenas + domaine
testSearchProposalsOfferBothEngines Chaque requête propose DuckDuckGo et Google
testGoogleSearchLoadsGoogle La recherche Google aboutit sur Google
testReaderSemanticZoomLevels Bandeau → lecteur → 4 niveaux → retour page brute
testSemanticHistoryRecallInAddressBar Visite → distillation → rappel « Déjà visité » → réouverture
testStructuredFavoriteFromReaderAppearsInLibrary Signet lecteur → favori en données natives dans la bibliothèque
bash
xcodebuild -project Prisme.xcodeproj -scheme Prisme \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  test CODE_SIGNING_ALLOWED=NO

# 🔒 Vie privée

  • Aucun serveur. Prisme n'opère aucun backend ; rien à héberger, rien qui fuite.
  • Aucun compte, aucune télémétrie, aucun identifiant.
  • IA 100 % on-device ; l'escalade cloud (Private Cloud Compute) sera explicite, visible, et interdite sans exception sur le contenu des conteneurs sensibles.
  • Recherche par défaut : DuckDuckGo ; Google proposé à chaque requête, jamais imposé.
  • L'univers « Recherche sensible » n'existe pas pour la mémoire : ni historique, ni index, ni snapshot.

# 🗺 Feuille de route

Ordre de construction strict — chaque étape solide avant la suivante :

  • 1. Navigateur nu, excellent — onglets, pool, gestes, blocage, conteneurs
  • 2. Distiller + cache — le cœur invisible dont tout dépend
  • 3. Zoom sémantique + rendu adaptatif — la démo
  • 4. Historique sémantique — la valeur qui s'accumule
  • 5. Favoris repensés (P0) — l'extrait, le favori structuré
  • 5bis. Favori vivant (BGTaskScheduler) + favori-question
  • 6. Vie privée avancée — patterns manipulateurs, traducteur de conditions
  • 7. Agent — portée étroite, jamais d'action irréversible sans confirmation

# 👤 Auteur

Simon-Pierre Bouchercontact@spboucher.ai

Licence MIT © 2026 Simon-Pierre Boucher

Construit en SwiftUI, distillé sur l'appareil, rien ne quitte votre iPhone.