Aller au contenu
SheetForge

Capacités et limites

Cette page liste tout ce que SheetForge ne fait pas, ne peut pas encore faire, ou fait différemment de ce à quoi vous pourriez vous attendre, avec la raison, la solution de contournement, et s'il existe une marge d'évolution future.

Format par entrée : Quoi / Pourquoi / Solution de contournement (+ marge d'évolution future si pertinent).


1. Plateforme et dépendances

Addressables est requis — le pipeline se verrouille sans lui

  • Quoi : le chargement par adresse est le chemin d'exécution, et le type AssetRef@Group a besoin de com.unity.addressables, le package doit donc être installé pour utiliser l'asset. L'asset lui-même compile sans lui — tout le code utilisant Addressables se trouve derrière un define de version SHEETFORGE_ADDRESSABLES qui ne s'active que lorsque le package est présent.
  • Comportement en son absence : tout le pipeline (import · export · push · écriture en retour de création) est verrouillé, pas dégradé — exécuter n'importe quel point d'entrée affiche un avis d'installation et s'arrête. Il n'existe aucun chemin partiel ou silencieux (pas de repli du type « ignorer la validation des clés d'asset »). Parce que l'Editor compile sans le package, il ne tombe jamais en Safe Mode. La fenêtre Prise en main s'ouvre normalement, et sa ligne Addressables affiche ✗ avec un bouton Open Package Manager. La fenêtre d'amorçage sans dépendance SheetForge.Setup demeure comme filet de guidage pour d'autres échecs de compilation, sans rapport.
  • Dans le Data Studio sans le package : le bouton du sélecteur ⊙ de la cellule d'asset et sa cible de glisser-déposer sont désactivés, avec la raison en infobulle ; taper une adresse dans la cellule fonctionne toujours.
  • Pourquoi pas une double abstraction Resources/Addressables : délibérément non construite — jugée comme de la sur-ingénierie.
  • Solution de contournement : installez Addressables. L'invite d'import de l'Asset Store s'en occupe avant la compilation ; si vous l'avez ignorée, l'Editor qui compile vous guide pour l'installer.
  • Vérification : les deux branches sont exercées en direct. Avec les defines de version retirés (simulant « Addressables absent »), le produit et les assemblies de test compilent avec 0 erreur ; restaurés, 0 erreur et 0 avertissement. Vérifié de façon indépendante de bout en bout en important l'asset dans un projet neuf sans Addressables installé : le projet compile et la fenêtre de guidage d'installation apparaît comme prévu.

Unity Localization est optionnel — seule la synchronisation StringTable l'attend

  • Quoi : les feuilles de localisation (@loc), les références LocRef, les constantes de clé, les rapports de couverture, Export, Push, xlsx et l'application web fonctionnent tous sans com.unity.localization installé. La seule chose qui attend est la sortie de synchronisation StringTable : elle affiche un avis d'installation (une fois par session) et s'arrête. Tout le code touchant au package se trouve derrière un define de version SHEETFORGE_LOCALIZATION, si bien que chaque assembly et chaque ligne de code généré compile sans le package — les champs générés sont la simple struct LocRef, jamais un type du package.
  • Comportement en son absence : rien ne se dégrade et rien n'est sauté silencieusement ailleurs — les feuilles restent des feuilles complètes ; seule la sortie de synchronisation est verrouillée, avec la raison affichée.
  • Version prise en charge : 1.5 ou plus récente.
  • Solution de contournement : installez le package quand vous voulez les tables ; tout ce qui a été rédigé avant ce moment se synchronise au prochain import terminé. Voir Feuilles de localisation.

Aucune installation programmatique en un clic

  • Quoi : la fenêtre de filet de sécurité vous guide ; elle n'installe pas le package elle-même. La même règle couvre l'avis d'installation d'Unity Localization.
  • Pourquoi : les règles de soumission de l'Asset Store limitent l'installation programmatique de packages ; une fenêtre de guidage est le choix sûr et conforme.

Le define SHEETFORGE de détection de produit n'est pas retiré automatiquement

  • Quoi : l'assembly Editor s'auto-enregistre comme symbole de compilation SHEETFORGE sur chaque plateforme cible, afin que d'autres assets puissent détecter que SheetForge est installé à la compilation (voir Création de plugins ▸ Détecter SheetForge depuis un autre asset). L'enregistrement est idempotent (ajouté seulement s'il manque — aucune recompilation superflue une fois présent).
  • Limite : si vous supprimez l'asset par la suite, ce define subsiste — le code qui aurait remarqué sa suppression disparaît avec lui.
  • Solution de contournement : retirez-le à la main dans Project Settings ▸ Player ▸ Scripting Define Symbols (par plateforme). Nous ne maintenons volontairement pas d'observateur en arrière-plan juste pour nettoyer un seul symbole. Ceci est distinct de SHEETFORGE_ADDRESSABLES, un define de version interne qui ne fait que refléter la présence du package Addressables.

2. Source Google Sheets

Le mode ExportUrl est en lecture seule

  • Quoi : Push, la répercussion, la modification de structure, et la suppression sont tous désactivés en mode ExportUrl.
  • Pourquoi : c'est le chemin d'export non authentifié, partagé par lien — en lecture seule par nature. Push exige toujours des identifiants SheetsApi, imposés avant tout appel réseau.
  • Solution de contournement : utilisez le mode SheetsApi (compte de service — voir Configuration des feuilles Google) pour toute écriture en retour.

ExportUrl requiert une gid map

  • Quoi : une gid map vide fait échouer l'import ; les gid en double sont rejetés.
  • Pourquoi : une URL d'export sans gid renvoie silencieusement seulement le premier onglet, si bien que l'import la rejette. SheetsApi découvre les onglets automatiquement.
  • Solution de contournement : enregistrez la valeur #gid= de chaque onglet, ou utilisez SheetsApi.

Push supprime les lignes par clé, et seulement celles vérifiées

  • Quoi : un enregistrement supprimé localement est retiré de la feuille en direct lors du Push — après que la récupération précédant l'envoi confirme que sa clé se trouve toujours sur la ligne vue par votre import. Une ligne déjà disparue compte comme faite (Push idempotent en cas de nouvel essai) ; une clé trouvée sur une ligne différente est ignorée avec un avis, jamais supprimée par position. Les suppressions sont listées dans leur propre section du résumé d'approbation et envoyées en dernier, de bas en haut par onglet.
  • Pourquoi : l'association par clé avec la feuille en direct est ce qui rend la suppression sûre sur une feuille qui a pu dériver ; tout ce que l'association ne peut pas confirmer est laissé tel quel.
  • Limite : une source incapable de supprimer des lignes (un fournisseur personnalisé qui n'a jamais acquis cette capacité) retombe sur l'ancien comportement — la suppression est signalée et la ligne en direct vous est laissée pour la retirer.

Push ignore les cellules en conflit (par conception)

  • Quoi : les cellules modifiées par un tiers depuis votre import, les lignes dont la clé a bougé de façon ambiguë, les lignes manquantes, ou les clés en direct en double sont ignorées avec des avertissements — jamais écrasées.
  • Pourquoi : c'est le filet de sécurité qui fonctionne : les cellules envoyées sont valides ; les cellules ignorées protègent les changements des autres et empêchent des écritures sur la mauvaise ligne.
  • Solution de contournement : vérifiez les compteurs appliqué/ignoré dans le rapport ; réimportez pour réconcilier, puis relancez un Push. (Une interface de résolution de conflit serait une fonctionnalité séparée — non prévue.)

Push requiert une colonne clé

  • Quoi : un onglet modifié sans colonne clé RecordId ne peut pas être poussé — c'est une erreur de plan qui bloque le Push tout entier (zéro envoi pour chaque onglet ; aucun envoi partiel).
  • Pourquoi : Push relocalise les lignes par clé dans la feuille en direct ; sans clé, le garde-fou contre les mauvaises lignes ne peut pas tenir.
  • Solution de contournement : ajoutez une colonne clé, ou exportez vers un fichier et collez.

3. Source xlsx

  • Le renommage d'onglet exclut les onglets d'origine xlsx — protection des classeurs multi-feuilles. Renommez dans le classeur, réimportez.

  • La propagation de renommage de clé vers un onglet d'origine xlsx bloque le lot entier — le chemin xlsx ne peut pas effectuer de mises à jour de cellule chirurgicales de façon sûre, et une répercussion partielle n'est jamais autorisée. Modifiez cet onglet directement, réimportez.

  • Les cellules non représentables sont rejetées — les cellules de formule sans valeur mise en cache, les cellules d'erreur, et les tabulations/sauts de ligne à l'intérieur d'une cellule. Le lecteur OOXML intégré est volontairement minimal (zéro code tiers). Matérialisez les formules ; utilisez ; pour les listes.

  • Valeurs uniquement — les formules, les dates et la mise en forme sont interprétées, honnêtement — une cellule de formule fournit sa valeur mise en cache (jamais recalculée), une cellule au format date est lue comme du texte affiché yyyy-MM-dd, et les formats numériques, les cellules fusionnées et les graphiques ne sont pas importés. La boîte de dialogue d'import de l'application web nomme ce qui s'est réellement produit dans une note « Comment ce classeur a été lu » ; dans l'éditeur, la même politique s'applique silencieusement par cellule (les rejets ci-dessus continuent de se signaler par cellule).

  • Certaines règles de liste déroulante exportées ne peuvent pas être portées — l'export est un classeur unique, si bien que la liste déroulante d'une colonne de référence s'écrit comme une vraie plage sur la colonne clé de la feuille cible, la même signification que porte la règle Google. Trois cas sont encore laissés de côté, et nommés ensemble dans un unique avertissement DropdownNotSupportedByFormat :

    • un membre de liste contenant une virgule (le séparateur en ligne le scinderait) ;
    • une liste en ligne dépassant la limite de spécification de 255 caractères (guillemets compris) ;
    • une plage dont l'onglet cible n'est pas dans le classeur.

    Les valeurs s'exportent intégralement dans tous les cas. Voir Sources, export et push.

4. Création — le Data Studio

Un onglet sans colonne clé ne reçoit aucun nouvel enregistrement, et ses modifications de valeur perdent leur ancre

  • Quoi : un onglet sans colonne clé RecordId s'importe et s'affiche normalement, et l'édition de structure fonctionne pleinement — ajouter, supprimer, renommer, réorganiser des colonnes et des marqueurs, plus le renommage et la suppression au niveau de la feuille. Ce qu'il ne peut pas gagner, c'est un nouvel enregistrement, puisqu'un enregistrement sans clé ne peut être ni nommé ni référencé :

    • le contrôle d'ajout de ligne est désactivé ;
    • le sélecteur de référence refuse d'y créer (« … n'a pas de colonne clé, donc aucun nouvel enregistrement ne peut y être créé ») ;
    • un canevas ou une action d'inspecteur qui tente d'y écrire ne fait rien.

    Les cellules de valeur sont éditables — mais sans clé pour adresser la ligne, la modification est préparée uniquement contre la position de la ligne.

  • Pourquoi : chaque modification préparée est normalement adressée logiquement, comme (tab, record key, field), et résolue à nouveau contre la feuille juste avant l'écriture. C'est ce qui permet à une modification de survivre à un réimport, à une réorganisation de ligne, ou à quelqu'un d'autre insérant des lignes au-dessus. Sans colonne clé, une telle adresse n'existe pas, donc la modification passe épinglée à un numéro de ligne à la place — en dehors de ce filet de sécurité.

    Donc une modification ancrée à une position peut atterrir sur la mauvaise ligne si les lignes de la feuille bougent sous vos pieds avant que vous n'écriviez en retour (un réimport, ou quelqu'un qui modifie la source directement). Préparez et répercutez cela par petits lots.

  • Solution de contournement : ajoutez une colonne RecordId (l'édition de structure est disponible, vous pouvez donc le faire dans la même fenêtre), répercutez, et l'onglet devient pleinement créable avec l'ancre logique de retour en place. Les onglets sans clé restent parfaitement valides à importer — c'est une limite de création, pas une limite de schéma.

Le renommage de colonne / le changement de @type casse le code de jeu qui référence ; irréversible après la répercussion

  • Quoi : le nom/type du champ généré change ; le code de jeu qui le référence doit être mis à jour à la main. Ctrl+Z ne fonctionne que avant la répercussion.
  • Pourquoi : le typage fort — le champ fait partie du schéma généré. Une rupture de compilation est captée par l'interruption sûre de la chaîne automatique, avec une phrase exploitable. Les valeurs de la colonne sont entièrement préservées (seules les cellules de marqueur changent).
  • Solution de contournement : la boîte de dialogue de confirmation avertit d'abord ; mettez à jour votre code et laissez le prochain import reprendre.

Le renommage d'onglet casse le code de jeu qui référence ; irréversible après la répercussion

Mêmes mécanismes que ci-dessus — le nom de la classe générée change (FooDatabaseBarDatabase) ; le réimport prend en charge automatiquement tout le nettoyage côté asset (ancienne classe, SO, adresse).

Les renommages mutuels (permutation) et cycliques d'onglet sont pris en charge

  • Quoi : Alpha→Beta + Beta→Alpha (une permutation), et des cycles plus longs (A→B→C→A), peuvent être préparés et répercutés en un seul lot. Préparer d'abord l'une ou l'autre moitié fonctionne, et la barre d'onglets affiche immédiatement les noms échangés (WYSIWYG, annulable). Le verrou de l'interface utilise l'unicité de l'ensemble des noms finaux — seul un véritable conflit, deux renommages ciblant le même nom, est rejeté. La répercussion l'impose strictement.
  • Les références suivent les données (l'identité de l'onglet), pas le nom : après une permutation A↔B, RecordId@A est réécrit atomiquement en RecordId@B (passage unique — jamais appliqué deux fois), si bien qu'il continue de pointer vers les mêmes données, qui ont déménagé vers B.
  • Local : une permutation échange le contenu des deux fichiers en une seule passe d'écriture. Une chaîne qui réutilise un nom à travers une extension différente supprime le fichier obsolète de l'ancienne extension (garde-fou de suppression fondé sur le chemin), si bien que le réimport ne voit jamais d'onglet en double.
  • Google : les changements de titre sont ordonnés topologiquement et cassent tout cycle avec un titre temporaire (A→tmp, B→A, tmp→B), si bien que la feuille en direct ne détient jamais un titre en double momentané. Si un changement de titre échoue en cours de séquence, l'onglet laissé sous un nom temporaire est signalé avec des conseils de récupération.

Cas particulier propre à Google : les onglets permutés qui se référencent l'un l'autre ne sont pas repointés

  • Quoi : lorsque les deux onglets permutés se référencent l'un l'autre — l'onglet A a une colonne RecordId@B et l'onglet B a une colonne RecordId@A —, le chemin Google préserve leur contenu via le changement de titre sur place et ne réécrit pas leurs propres cellules @type. Cette référence mutuelle n'est donc pas repointée sur Google.
  • Pourquoi : Google renomme un onglet en changeant son titre (contenu inchangé, par conception) ; réécrire la propre grille de l'onglet renommé annulerait cela. Les sources locales réécrivent la projection de l'onglet renommé, si bien que le mode local gère cela entièrement. Les références provenant d'un troisième onglet sont repointées sur les deux chemins.
  • Solution de contournement : sur Google, faites transiter la référence mutuelle par un troisième onglet, ou répercutez la permutation via un nom intermédiaire.

La surcouche de SO à valeurs préparées n'a plus de bouton

  • Quoi : la surcouche « prévisualiser les valeurs préparées sur les SO » (EphemeralSoApply) était pilotée par un bouton de l'Espace de travail, et cette fenêtre a disparu. Le type reste une API publique pour un outil qui en a besoin ; l'interrupteur Modification test de l'inspecteur de SO couvre le cas courant où l'on veut essayer un chiffre à l'exécution.
  • Limite si vous l'appelez quand même : la surcouche refuse — avec un badge — (a) les colonnes en attente/nouvelles et (b) les cellules en échec de parsing. Les nouvelles lignes sont prises en charge. Elle réutilise le véritable chemin de parsing+bake, si bien que ce qu'elle ne peut pas calculer fidèlement, elle le refuse plutôt que de le simuler.
  • Solution de contournement : ce n'a toujours été qu'un aperçu ; répercutez normalement pour le changement réel. Le réimport restaure toujours la vérité.

Les modifications préparées s'isolent quand leur ligne est renommée, supprimée, ou entre en conflit de clé à l'extérieur

  • Quoi : une modification préparée dont la ligne a été renommée à l'extérieur, supprimée à l'extérieur, ou entrée en conflit de clé entre la préparation et la répercussion, est exclue de la répercussion et signalée par un badge « isolée ».
  • Pourquoi : son adresse logique ne peut pas être résolue à nouveau — mais elle n'est ni abandonnée silencieusement, ni autorisée à bloquer la session.
  • Solution de contournement : abandonnez-la individuellement (après confirmation) et repréparez-la.

La propagation de renommage de clé ne couvre que les cellules de la baseline

  • Quoi : le texte que vous venez de saisir dans le même lot et qui référence l'ancienne clé n'est pas réécrit automatiquement.
  • Pourquoi : réécrire silencieusement la saisie fraîche de l'utilisateur est interdit ; la validation préalable détecte la référence pendante à la place.
  • Solution de contournement : corrigez vous-même la référence préparée, ou répercutez d'abord le renommage.

Les entrées restantes concernent toutes le Data Studio, l'unique fenêtre de création. L'ancienne fenêtre Espace de travail a été retirée ; les trois fonctionnalités qu'elle seule offrait ont d'abord été déplacées dans le Studio et l'inspecteur de paramètres — voir ce qu'est devenu l'Espace de travail.

Une feuille en échec de validation s'ouvre pour édition — mais Export, Push et les builds restent bloqués

  • Quoi : si la source a été lue entièrement, ses feuilles sont enregistrées comme baseline même quand la validation a échoué, si bien que le Studio peut les ouvrir et que vous pouvez corriger les erreurs sur place. La génération de code et le bake ne s'exécutent pas tant que le nombre d'erreurs n'atteint pas zéro, et Export, Push vers la feuille en direct, et les builds joueur sont tous refusés tant que la feuille est dans cet état, chacun en disant la raison.
  • Pourquoi : ces trois sorties combinent toutes les dernières valeurs bakées avec succès avec les feuilles plus récentes. En exécuter une maintenant insérerait des valeurs périmées par-dessus des cellules que quelqu'un a déjà corrigées — une régression silencieuse. Bloquer les sorties est ce qui permet de laisser l'entrée ouverte.
  • Répercuter une correction ne demande qu'une fois : sur une feuille en quarantaine, l'écriture en retour affiche une confirmation supplémentaire, car la validation préalable ne peut pas y être la barrière stricte (la feuille a déjà des erreurs). Tout ce qui est trouvé est signalé comme avertissement dans cette répercussion, et le réimport automatique revalide la feuille entière. Les feuilles saines ne sont pas affectées — la validation préalable refuse toujours d'écrire.
  • Solution de contournement : corrigez chaque erreur signalée et retirez à nouveau. Le blocage se lève de lui-même au seul endroit qui l'efface, une exécution qui va jusqu'au bout du bake.

Le tri et le filtre du Data Studio sont pour l'affichage seulement — et désactivent la réorganisation de ligne tant qu'ils sont actifs

  • Quoi : le tri par feuille du Studio (n'importe quelle colonne, asc/desc, persisté par projet) et le filtre de texte ne changent que l'ordre d'affichage. La gouttière conserve les numéros de ligne réels de la feuille, et ni l'un ni l'autre n'affecte la préparation, la répercussion, le push, ou l'export. Pendant qu'un tri ou un filtre est actif, les outils de réorganisation de ligne ▲▼ sont désactivés avec une infobulle.
  • Pourquoi : réorganiser par « voisin visible » pendant que la vue est triée ou filtrée déplacerait silencieusement des lignes à côté de lignes que l'utilisateur ne peut pas voir. Les vrais changements d'ordre de ligne sont une opération de structure — videz d'abord le tri/filtre.
  • Remarque : « trier par plus récent » n'existe que si votre feuille a une colonne qui l'encode (par ex. un IntId ou une colonne de type chaîne façon date) — la feuille elle-même ne stocke aucun horodatage.

Les problèmes du Data Studio sont un brouillon tant qu'un renommage de clé est préparé

  • Quoi : tant qu'une cellule de clé (RecordId) a une modification préparée, le panneau Problems porte un badge draft, et les entrées de référence non résolue qu'il contient peuvent être de fausses alertes.
  • Pourquoi : l'aperçu en mémoire n'applique pas la propagation de renommage de clé — celle-ci s'exécute au moment de la répercussion, à travers chaque onglet. Plutôt que de cacher les diagnostics ou de simuler la propagation, la fenêtre vous dit que la liste est un brouillon jusqu'à ce que le renommage soit écrit.
  • Solution de contournement : répercutez le renommage (la propagation s'exécute avec sa propre confirmation), puis lisez la liste rafraîchie.

La table est virtualisée par ligne au-delà de 200 lignes — avec deux points particuliers à connaître

  • Quoi : au-delà de 200 lignes, la table ne construit les éléments de ligne que pour la fenêtre visible (plus douze lignes de marge de balayage), avec des espaceurs au-dessus et en dessous qui portent la vraie hauteur totale pour que la barre de défilement ne mente pas. Défiler à travers une limite de fenêtre réutilise les lignes qui survivent et ne construit que celles qui viennent d'entrer. La grille du navigateur fait de même, au même seuil.

    Deux cas construisent quand même tout. À 200 lignes ou en dessous, chaque ligne est construite exactement comme avant, bit pour bit. Il en va de même pour une table dont la hauteur de fenêtre visible ne peut pas du tout être demandée — une table hors d'une fenêtre, où la disposition n'arrive jamais — car là, le repli honnête est « tout construire ». Une grande table qui n'a simplement pas encore été disposée attend une image à la place, si bien qu'elle est virtualisée dès son premier rendu plutôt que de tout construire pour le jeter ensuite.

  • La ligne que vous éditez reste vivante même après avoir défilé hors champ, si bien que le curseur, le focus et ce que vous avez tapé survivent. Ce maintien en vie a un plafond de distance, et au-delà, l'éditeur ouvert valide et perd le focus plutôt que d'être porté indéfiniment. Rien n'est perdu quand cela arrive — la valeur est déjà dans la session de préparation.

  • Seule la création d'élément est virtualisée. L'échantillonnage de largeur de colonne, la recherche, le tri, les coordonnées et la surcouche de préparation considèrent toujours chaque ligne, car chacun d'eux donnerait une réponse différente s'il ne regardait que ce qui est à l'écran. Donc basculer vers une très grande feuille fait toujours un travail proportionnel à sa taille ; ce qu'elle ne fait plus, c'est construire des milliers de widgets.

  • Dans le navigateur, le mode virtualisé mesure les colonnes au lieu de laisser la disposition le faire. Des largeurs à disposition automatique se calculeraient à partir des lignes qui se trouvent dans la fenêtre, si bien qu'une colonne tressauterait au fil du défilement. En mode virtualisé, les largeurs proviennent d'une estimation pilotée par les données sur toutes les lignes, puis sont fixées. Le mode de rendu complet (≤ 200 lignes) utilise toujours la disposition automatique, inchangé.

Le canevas ne se déplace que dans sa plage de défilement, et ses fils de cycle en pointillé s'épaississent à mesure qu'ils grandissent

  • Quoi :

    • Ctrl/Cmd + molette de souris zoome le canevas d'enregistrements entre 25 % et 200 %, en gardant fixe le point sous le curseur — un zoom ancré au centre ferait glisser hors écran la carte que vous regardiez. Le pourcentage dans l'en-tête du canevas est un bouton qui revient à 100 %. Une simple molette continue de faire défiler.
    • Faire glisser avec le bouton central de la souris — ou Alt + bouton gauche, pour le matériel qui n'en a pas — déplace la vue (pan), et le curseur marque la prise tant qu'elle est maintenue. Le glissement au bouton gauche reste réservé à la sélection et à la liaison, il ne pouvait donc pas aussi signifier « déplacer la vue ».
    • Le panneau est une vue à défilement, donc la plage de déplacement est la plage de défilement : elle s'arrête au bord du contenu au lieu de dériver dans un espace vide, et quand le contenu est plus petit que la zone visible, elle ne bouge pas du tout. Ce n'est pas un canevas infini.
    • Un fil en pointillé marquant un cycle plafonne le nombre de tirets qu'il dessine et double sa période de tirets sur un chemin long, si bien qu'une très longue boucle se lit plus grossièrement plutôt que plus finement.
  • Pourquoi : Unity alloue des sommets de maillage par appel de rendu avec un plafond dur de 65,535, et le dépasser fait disparaître entièrement le dessin tout en payant quand même pour la tessellation. Le plafond de tirets garde un Stroke à l'intérieur de ce budget par conception.

    La grille de points d'arrière-plan se trouvait autrefois sur la même falaise et ne s'y trouve plus. C'est une petite tuile d'arrière-plan répétée, qui coûte zéro sommet et se redessine en temps constant quelle que soit la taille du canevas. (Un point dessiné comme un chemin coûte mesurément 28 sommets, pas ses quatre coins. C'est l'arithmétique derrière le budget de 1,800 points du repli de dessin, et la raison pour laquelle la tuile est le chemin livré.)

  • Solution de contournement : aucune nécessaire pour la grille. Pour un grand voisinage, dézoomez, réduisez le segment de direction, ou ouvrez un voisin comme nouveau terminus plutôt que d'essayer de tout faire tenir sur un seul écran.

Une feuille sans table pour l'instant est sautée, pas importée

  • Quoi : un onglet qui n'a aucun des trois marqueurs obligatoires et aucune ligne de données — une feuille flambant neuve ne portant que des commentaires ou une ligne @style — est sauté avec un avertissement EmptyTabSkipped. Il ne fait pas échouer l'import sur trois marqueurs manquants. Son code déjà généré, son asset baké et son adresse sont préservés, non nettoyés comme si l'onglet avait été supprimé. Export et Push le sautent symétriquement, car tous trois interrogent le même prédicat.
  • Pourquoi : une feuille inachevée ne doit pas pouvoir empêcher chaque autre onglet de s'importer, et un auteur crée généralement la feuille avant sa ligne d'en-tête.
  • Limite : une feuille à moitié écrite (un marqueur obligatoire quelconque présent) n'est pas sautée — elle échoue honnêtement, car la sauter silencieusement cacherait du vrai travail. Une feuille typée depuis la colonne A plutôt que la colonne B est de même remise au parseur, si bien que son vrai diagnostic (« la colonne A est la colonne des marqueurs, les données commencent en B ») survit.

Un enum enregistré depuis du C# de plugin ne peut pas gagner de membres depuis une feuille

  • Quoi : un Enum<T> dont le T a été enregistré par un plugin avec enums.Register<T>() est possédé par le code. Une feuille de définition d'enum ne peut pas revendiquer ce nom (DuplicateEnumName), et la ligne « Add a new member… » de la liste déroulante de cellule est simplement absente sur une telle colonne.
  • Pourquoi : la feuille n'est canonique que pour ce que la feuille définit. Écrire un membre dans une feuille qui ne décide plus du type compilé produirait un membre qui n'apparaît jamais dans le code — une promesse que le produit ne peut pas tenir. La ligne absente est la façon dont l'UI le dit, plutôt que d'offrir une action vouée à l'échec.
  • Solution de contournement : déplacez l'enum dans une feuille d'enum si la feuille doit le posséder, ou ajoutez le membre dans le C# de votre plugin et recompilez.
  • La structure suit la même ligne de propriété : la structure d'une feuille de définition d'enum est entièrement rédigée dans les deux hôtes — définir, renommer, supprimer, réorganiser les colonnes, éditer le type sous-jacent et la description — mais rien de tout cela ne peut toucher un nom d'enum possédé par du code, et une feuille de définition ne peut pas non plus revendiquer un tel nom. Le refus en nomme la raison.

Feuilles de définition d'enum : les membres ne font que s'ajouter, et il n'y a pas de tri

  • Quoi : la structure de la feuille d'enum est rédigée sur place, aussi bien dans l'éditeur que dans l'application web, mais les lignes de membre ne font toujours que s'ajouter — un trou n'est jamais rebouché — et la vue n'offre ni tri ni filtre.
  • Pourquoi : la position d'un membre est sa valeur entière. Combler un trou ou réorganiser les membres renumériserait silencieusement des valeurs déjà bakées dans des assets et stockées dans des sauvegardes. L'ordre des colonnes, en revanche, ne porte aucune signification, ce pourquoi réorganiser les colonnes est toujours autorisé.
  • Solution de contournement : pour fixer une valeur explicitement, utilisez la syntaxe Name=value ; l'ordre de présentation ailleurs est l'affaire du consommateur, pas celle de la feuille.

Un enum défini par feuille génère toujours dans le dossier des paramètres

  • Quoi : les types d'onglet générés se régénèrent sur place, dans quel que soit le dossier où ils vivent déjà. Le fichier d'enum (SheetForgeEnums.cs) n'a aucun onglet où s'ancrer, si bien qu'il est toujours écrit dans le dossier de code généré nommé dans les paramètres. Si un onglet dont le code généré vit dans son propre dossier de package utilise un enum défini par feuille, cet assembly de package échoue à compiler avec CS0246.
  • Pourquoi : un seul fichier porte chaque enum défini par feuille, car un enum est une sortie au niveau du projet plutôt que par onglet — il n'y a donc aucun onglet unique dont il pourrait suivre le foyer.
  • Solution de contournement : placez les deux dossiers générés dans un seul assembly, ou enregistrez plutôt cet enum depuis du code de plugin. L'échec est une erreur de compilation visible nommant le type manquant, jamais une corruption silencieuse.

Les références d'asset typées se résolvent contre les types chargés — nom court seulement si unique, pas de types d'assembly prédéfini

  • Quoi : AssetRef@Group<Type> accepte n'importe quel type d'asset dérivé d'UnityEngine.Object que le projet peut charger, moteur ou le vôtre, sans liste blanche. Trois choses sont refusées plutôt que devinées : un nom court que partagent plusieurs types chargés (AmbiguousAssetTypeTextAsset peut en être un, selon les packages installés) doit être écrit comme le nom complet (UnityEngine.TextAsset) ; un nom inconnu est UnknownAssetType avec une suggestion de correspondance la plus proche ; et un type qui vit dans un assembly prédéfini (Assembly-CSharp et ses semblables — tout dossier de script sans assembly definition) est AssetTypeNotReferenceable.
  • Pourquoi : l'assembly compagnon généré est une assembly definition, et une assembly definition ne peut pas référencer les assemblies prédéfinis — AssetReferenceT<T> pour un tel T ne compilerait pas. Résoudre un nom ambigu en en choisissant un lierait silencieusement la colonne au mauvais type.
  • Solution de contournement : déplacez le type dans une assembly definition, ou retirez la restriction <…> et gardez AssetRef@Group non restreint. Les composants et les types réservés à l'éditeur ne sont jamais candidats.
  • Aussi : le navigateur ne résout pas les noms de type (il n'a pas de projet contre lequel résoudre) : l'application web analyse <Type> et le montre dans l'infobulle de colonne, mais ne produit aucun des trois diagnostics de nom de type et n'offre ni sélecteur ni dépôt. Le codegen n'émet jamais un nom non résolu tel quel — un nom qu'il ne peut pas résoudre se replie sur AssetReference avec un avertissement AssetTypeUnresolvedFallback.

Le sélecteur d'asset, le dépôt et les enregistrements préparés — ce qui est automatique et ce qui ne l'est pas

  • Quoi : déposer ou choisir un asset écrit l'adresse dans la cellule aussitôt et prépare le changement Addressables (ajout · déplacement · création de groupe) pour la répercussion ; le changement ne s'exécute qu'après que l'écriture de la feuille a réussi — ou, quand les enregistrements sont le seul élément préparé, de façon autonome, suivis du réimport automatique ; une répercussion qui n'a pas pu écrire parce que ses onglets sont d'origine classeur les garde préparés. Un enregistrement que plus rien ne référence est abandonné quand la cellule est retapée, et tout enregistrement qui survit jusqu'à la répercussion est sauté comme non référencé ; un asset déjà dans le groupe conserve son adresse existante ; un asset dans un autre groupe n'est déplacé qu'après une confirmation qui nomme les autres cellules qui le référencent. L'adresse automatique est le nom de fichier sans extension, et une adresse déjà utilisée par un autre asset dans ce groupe est refusée plutôt que renommée. Les nouveaux groupes reçoivent les BundledAssetGroupSchema et ContentUpdateGroupSchema par défaut. Les éléments appliqués et sautés sont journalisés dans la console avec leur raison, et pour une source dossier local, la boîte de dialogue de fin de la répercussion répète le résumé (Addressables: N registered, M skipped).
  • Pourquoi : la feuille est canonique — le projet ne doit jamais changer pour une répercussion qui n'a pas atteint la feuille, et une entrée que rien ne désigne serait un orphelin que la feuille n'explique pas.
  • Solution de contournement : si un enregistrement a été sauté, le réimport suivant signale la cellule comme UnknownAssetKey ; corrigez la cause et répercutez à nouveau. L'enregistrement nécessite toujours le package Addressables.

Les sous-assets sont adressés comme parent[sub], et une texture en mode Sprite passe <Sprite>

  • Quoi : une entrée de sous-objet (un sprite dans une texture, un matériau dans une police) est adressée comme Addressables la nomme — parent[sub] — et cette clé est vérifiée contre le propre type du sous-objet. L'adresse parente satisfait son propre type et chaque type de sous-asset qu'elle contient, ce qui est ce qui permet à une texture importée en mode Sprite de passer une colonne <Sprite>. Déposer un sous-asset prépare le parent pour l'enregistrement et écrit parent[sub] dans la cellule.
  • Limite : une entrée de sous-objet doit exister dans le catalogue Addressables pour que parent[sub] se valide ; le sélecteur liste les sous-clés qu'il connaît après leur parent.

Les couleurs n'ont pas de HDR, les tangentes de courbe suivent leur mode, les dégradés sont quantifiés — par conception

  • Quoi : Color est quatre octets — un canal au-dessus de 1 (HDR) est borné à 0…1 à l'Export. Une tangente AnimationCurve dont le côté est Auto, Linear, Constant ou ClampedAuto est recalculée depuis le mode à l'import, si bien qu'un nombre saisi à la main qui contredit son mode est remplacé (le même recalcul qu'effectue Unity quand le mode est appliqué) ; Once est lu comme ClampForever et n'est jamais réécrit ; une courbe sans clé n'a pas de forme de texte et n'existe que comme la cellule vide d'une colonne optionnelle. Les temps de clé Gradient sont quantifiés sur 16 bits à l'import (exactement comme Unity les stocke), un dégradé à une seule clé revient d'Unity sous la forme de deux clés identiques, et l'espace colorimétrique n'est écrit que lorsqu'il a été défini.
  • Pourquoi : la valeur que montre la feuille doit être la valeur que détient le moteur, si bien que la normalisation qu'effectuerait Unity plus tard est effectuée une fois, à l'entrée, et chaque surface — feuille, champ d'éditeur, aperçu web, asset baké — montre une seule courbe et un seul dégradé.
  • Solution de contournement : utilisez Free/Free quand vous voulez que les nombres de tangente soient pris littéralement ; stockez les intensités HDR dans une colonne float séparée.

Les éditeurs à puces et les champs natifs n'existent que pour les trois types visuels

  • Quoi : le Data Studio montre les scalaires Color, AnimationCurve et Gradient comme les propres champs d'Unity et leurs List<> comme des éditeurs à puces ; l'application web montre des aperçus avec ses propres éditeurs et des listes à puces. Toute autre colonne de liste — List<int>, List<Enum<…>>, les listes de wrapper — reste du texte canonique dans les deux hôtes, et un wrapper contenant l'un des trois (Pair<Color>) est aussi du texte.
  • Pourquoi : les trois types sont ceux qui ont une image par élément ; pour le reste, une seule ligne canonique est déjà la représentation la plus exacte, et la notation externe d'un wrapper appartient à son plugin.
  • Marge d'évolution : un type de plugin qui stocke l'une des trois valeurs peut adopter les mêmes éditeurs en déclarant l'archétype StudioCellEditorHint correspondant (voir Création de plugins §4.16).

La coloration de propriété est uniquement feuille-versus-code

  • Quoi : la barre latérale/légende distingue exactement deux origines — les vrais onglets de feuille et les onglets virtuels de registre de code. Il n'y a pas de troisième classification « générée », et pas de coloration de propriété par colonne.
  • Pourquoi : l'origine est dérivée de l'onglet lui-même, que la fenêtre connaît déjà avec certitude ; une classification par colonne aurait besoin d'encore un autre point d'extension pour être fidèle, et aucun consommateur n'en a demandé une.
  • Pas la même chose que la couleur d'une feuille : @style permet à une feuille de nommer sa propre couleur, et cette couleur est une métadonnée d'affichage que l'auteur a choisie — elle ne dit rien sur la provenance de la feuille. Les deux colorations sont lues depuis des endroits différents et ne fusionnent jamais.

Les listes déroulantes de référence répondent à l'appartenance, pas à l'ordre

  • Quoi : une cellule RecordId@Tab a désormais une liste déroulante cherchable (et une liste à cocher pour List<>), mais la liste déroulante de la cellule de liste ne fait qu'ajouter et retirer des éléments — elle ne peut pas en déplacer un. Réorganiser une liste se fait sur le canevas, où chaque élément a sa propre ligne.
  • Pourquoi : une liste déroulante répond à « qu'y a-t-il ici » ; « quelle position » a besoin d'une surface qui aligne les choses, ce que le canevas est déjà. Le dupliquer à deux endroits ferait deux réponses à maintenir.
  • Aussi : la liste déroulante ne s'attache qu'à une colonne de référence ordinaire, non-wrapper. Le texte d'une cellule wrapper porte la propre notation du wrapper, donc y coller une clé nue détruirait la valeur — atteindre l'intérieur d'un wrapper est le travail d'un widget de cellule enregistré (voir Création de plugins).

La recherche All lit les données bakées dans l'éditeur et la session en direct dans le navigateur

  • Quoi : l'entrée All du Data Studio recherche dans les bases de données bakées, elle a donc besoin d'un premier import réussi et se rafraîchit d'elle-même quand un import se termine ou que les paramètres actifs changent. L'entrée All de l'application web recherche dans les valeurs que la session montre à l'instant, modifications préparées incluses. La correspondance, l'ordre des résultats et les 50 lignes par page sont le même code des deux côtés, et des deux côtés un résultat s'ouvre par double-clic (ou Entrée sur la ligne sélectionnée) pour ouvrir cette feuille avec la cellule correspondante sélectionnée.
  • Pourquoi : le navigateur n'a pas de ScriptableObjects bakés ; ce qu'il a, c'est la session en direct — et répondre avec la valeur affichée à l'écran, c'est à cela que sert une session de navigateur. L'éditeur continue de lire la vérité bakée qu'il possède déjà.
  • Limite : dans l'éditeur, une modification préparée mais pas encore répercutée n'est pas trouvée par All tant qu'un import n'a pas été exécuté, et un résultat provenant d'une feuille que la session n'a pas chargée ne navigue pas — un avis sous la liste en dit la raison. Dans le navigateur, une modification préparée est trouvée aussitôt et chaque résultat peut être atteint.

5. Performance

  • Le chemin normal est linéaire et rapide : 50,000 lignes × 20 colonnes ≈ 628 ms (éditeur en direct, Mono ; 144 ms en headless) ; 50 onglets × 2,000 lignes avec 180k cellules de référence ≈ 294 ms. Les tailles de projet typiques ne posent aucun problème.
  • La mémoire est linéaire mais gourmande en boxing : ≈ 59 bytes/cellule conservés (≈ 138 au pic pendant l'import). À la limite de cellules d'une Feuille Google (~10M cellules), cela s'extrapole à ~6.3 s d'import, ~590 MB conservés, ~1.4 GB au pic — attention aux contextes bas de gamme/32 bits pour les tailles extrêmes. (Un IR orienté colonnes est un élément reconnu du backlog.)
  • La table de création est virtualisée par ligne au-delà de 200 lignes, aussi bien dans l'éditeur que dans le navigateur, si bien qu'ouvrir une grande feuille ne construit plus un widget par ligne. Ce qui n'est pas virtualisé, c'est la logique par ligne qui répondrait différemment si elle ne regardait que ce qui est à l'écran — l'échantillonnage de largeur, la recherche, le tri, les coordonnées, la surcouche de préparation. Les détails et les deux points particuliers sont dans §4.
  • Le chemin d'erreur est linéaire lui aussi : même quand des références se cassent en masse, le calcul des suggestions de correspondance la plus proche reste borné. Un budget de suggestions par champ, plus une distance d'édition préfiltrée par longueur et à terminaison anticipée, le maintiennent à peu près linéaire par rapport au nombre de références cassées (≈ 45 ms pour 4,000 références cassées, en headless ; données valides à la même échelle ≈ 2.7 ms). Renommer un onglet référencé ne casse d'ailleurs pas les références en masse en premier lieu : le renommage réécrit les cellules @type qui référencent.

6. Scène de démonstration

  • Les exemples sont des imports sélectifs — les deux exemples de démo et leurs scènes ne sont pas présents par défaut. Ils sont livrés sous forme de packages Unity (Assets/SheetForge/Examples/SheetForgePluginDemo.unitypackage et SheetForgeCoreDemo.unitypackage). Importez-en un — double-clic, ou le bouton Importer le Plugin/Core Demo de la fenêtre Prise en main — pour restaurer Assets/SheetForge.PluginDemo/… ou Assets/SheetForge.CoreDemo/…. Jusque-là, les exemples ne sont pas du tout présents dans votre projet — ils ne sont distribués que sous forme de ces packages — si bien qu'ils ne peuvent jamais entrer en collision avec votre projet. Le produit principal est entièrement autosuffisant sans eux.
  • Nécessite un import préalable (par machine) — les adresses Addressables qu'elle charge sont un cache non commité. Avant cela, elle affiche un message de guidage.
  • Aucun réglage d'espace de noms n'est nécessaire pour les démos — les types d'exemple commités utilisent l'espace de noms par défaut SheetForge.Generated avec un préfixe de classe Example*, si bien que réimporter une démo les régénère sur place sans aucun réglage de generatedNamespace.

7. Extension par plugin — coutures livrées (avec leurs limites) et ce qui reste réservé

Seize contrats d'extension sont livrés, chacun rejoignant le produit avec zéro modification du Core — l'ensemble complet se trouve dans Création de plugins. Les seize sont trouvés par découverte (le TypeCache d'Unity ; le scan d'assembly téléversé du navigateur), sans référence d'assembly ni manifeste à éditer. Les onze contrats du Core reçoivent un registre où enregistrer ce qu'ils ajoutent, tandis que les cinq contrats Editor — widget de graphe, action d'inspecteur, fournisseur d'éditeur de cellule, fournisseur de panneau et fournisseur de source — sont simplement découverts et utilisés tels quels.

Cinq des coutures livrées portent des limites qui méritent d'être énoncées ici plutôt que de vous laisser les découvrir :

Types de cellule référençants — parité complète avec RecordId@Tab pour votre propre notation

  • Quoi : un parseur de cellule enregistré qui implémente aussi IReferencingCellType (et dont la valeur implémente IRefBearingValue) indique au Core comment lire et réécrire la clé enfouie dans sa propre notation. Cette colonne reçoit alors tout ce que reçoit une référence intégrée :

    • vérification d'intégrité avec suggestions de correspondance la plus proche ;
    • propagation de renommage de clé qui préserve la charge utile (attack:add:10power:add:10) ;
    • arêtes et ports de graphe, le sélecteur , comptes de référence en retour ;
    • détection d'orphelins et règles de liste déroulante exportée.

    La découverte est un cast du parseur déjà enregistré — il n'y a pas de nouveau canal d'enregistrement, et un type personnalisé qui ne l'implémente pas reste inchangé bit pour bit. Voir Création de plugins §4.4a.

  • Limites : une charge utile ne peut pas contenir ; — le Core découpe une cellule de liste en éléments avant que votre parseur ne voie jamais le texte, si bien qu'un point-virgule à l'intérieur d'une valeur serait déchiqueté en deux éléments (la même contrainte que portent les types wrapper). Et @target doit nommer un véritable onglet de feuille : l'onglet virtuel d'un registre de code est rejeté avec UnknownTargetTab, exactement comme pour RecordId@Tab. Cette restriction est ce qui permet au propre signalement de référence non résolue et à la propagation de renommage du Core de s'appliquer sans modification.

Les types wrapper <> (MyWrapper<T>) — avec des règles de rejet

  • Quoi : un plugin enregistre une forme de valeur générique (par ex. Pair<int> = 1~2) via ICellWrapperType ; le Core résout le type interne de façon récursive (voir Syntaxe des feuilles).

  • Règles de rejet :

    • Pair<List<T>> est rejeté — une liste ne peut pas se trouver à l'intérieur d'un wrapper, et List reste plat et le plus externe.
    • Pair<int>@Tab est rejeté — placez le @ sur la feuille interne : Pair<RecordId@Tab>.
    • Pair<int?> / Pair<int=1> sont rejetés — l'optionalité/les valeurs par défaut sont de niveau champ, pas une partie du type interne.

    List<Pair<T>> est autorisé, mais le délimiteur propre au wrapper doit différer de ; (le séparateur de liste) — une responsabilité de l'auteur du plugin que le Core ne peut pas imposer.

Les marqueurs structurels personnalisés (@yourMarker) — uniquement pour des métadonnées par colonne

  • Quoi : un plugin enregistre une ligne @marker via IStructuralMarkerDefinition / ISheetForgeMarkerPlugin, généralisant la validation par colonne de @overlap. La valeur est stockée comme métadonnée FieldSchema.MarkerValues.
  • Limites : un marqueur ne possède que sa validation de valeur par colonne — il ne prend pas en charge le parsing d'une forme de données sur la ligne entière (la normalisation reste le moyen d'exprimer les formes de données). Et le codegen ne bake pas les valeurs de marqueur : comme @overlap, ce sont uniquement des métadonnées de validation/affichage, invisibles pour l'empreinte de schéma — si bien que rien de lié aux marqueurs n'atteint le code généré ou la SO bake.

Préréglages de couleur — les surfaces que nous peignons, pas les widgets d'Unity

  • Quoi : un plugin enregistre un préréglage de couleur via ISheetForgeThemePlugin / ThemeRegistry. Il apparaît dans Preferences ▸ SheetForge ▸ Theme à côté des préréglages intégrés Default et High contrast, et ne s'applique que si l'utilisateur le choisit (enregistrer ne détourne jamais l'écran). Un préréglage ne surcharge que les slots qu'il nomme — chaque autre slot garde la valeur par défaut du produit, si bien que les préréglages restent valides à mesure que des slots sont ajoutés.
  • Limite — un habillage mixte est attendu : le thème couvre ce que SheetForge peint lui-même (arrière-plans de fenêtre, en-têtes, texte, accents, couleurs de grille et de préparation). Les widgets Unity natifs dessinés à l'intérieur de ces fenêtres — chrome des boutons, bordures de champ, flèches de popup — continuent de suivre le skin de l'éditeur, qu'Unity ne laisse pas un package restyler. Donc choisir Always light pendant que l'éditeur exécute le skin sombre donne une surface SheetForge claire avec des widgets natifs sombres dessus. Réglez le skin de l'éditeur en conséquence si vous voulez un aspect uniforme.
  • Limite — couleur uniquement : les préréglages portent des couleurs (0xRRGGBB par slot). L'espacement, les tailles de police et la disposition ne sont pas thémables, et les remplissages translucides (arrière-plans de badge, voile de la fenêtre modale) dérivent d'une couleur de slot plus un alpha fixe plutôt que d'être réglables séparément.

Surfaces de création déclaratives — un vocabulaire borné, volontairement

  • Quoi : ISheetForgeStudioPlugin permet à un pack de décrire des verbes, des panneaux, des badges de colonne et des formes d'éditeur de cellule comme données, si bien qu'un seul enregistrement se dessine à la fois dans l'éditeur et dans le navigateur. Le vocabulaire est fixe et ne grandit qu'en ajoutant : cinq emplacements d'action, treize genres de nœud, sept archétypes d'éditeur de cellule (les deux plus récents, CurveEditor et GradientEditor, sont ceux qu'utilisent les types intégrés de courbe et de dégradé).
  • Limite — ce n'est pas un framework d'UI. Le rendu arbitraire, la saisie composite et les flux à plusieurs étapes n'ont aucun mot ici, et les ajouter signifierait maintenir pour toujours une boîte à outils d'UI miniature. C'est à cela que sert IStudioPanelProvider : enregistrez-le sous le même id qu'un panneau décrit et l'éditeur dessine la version riche tandis que le navigateur dessine la version décrite. Il n'existe aucune échappatoire réservée au web — le navigateur ne peut pas charger un type UIToolkit, et prétendre le contraire mettrait l'extension d'un plugin sur un seul écran.
  • Limite — les pouvoirs d'une action sont exactement quatre : préparer une cellule, préparer plusieurs cellules comme une seule étape d'annulation, mettre le focus sur un enregistrement, demander un redessin. Le verbe d'un plugin est donc une modification préparée ordinaire qui passe par la même barrière, la même validation préalable et le même push qu'une modification tapée à la main. La session de création elle-même n'est délibérément pas exposée.
  • Zéro-déclenchement, jamais de faux-déclenchement, pour les observateurs : IPipelineObserver se déclenche à la fin d'un cycle d'import explicite. Deux chemins n'atteignent jamais du tout ce point — une exécution qui s'arrête avant que le pipeline ne démarre (aucun paramètre actif ; Addressables non installé) et un segment codegen→compilation interrompu par une erreur de compilation. Si vous avez besoin de « un import a été tenté », associez ceci au bus ImportEvents côté éditeur.

Encore conçu mais non construit (pas encore de consommateur)

  • Les marqueurs de forme de données sur ligne entière (par ex. un marqueur qui lirait une matrice 2D comme un seul champ) — volontairement non construits : un marqueur possède la validation par colonne, pas le parsing de ligne, et la normalisation (références + colonne type + List<T>) est expressivement complète. La couture d'enregistrement MarkerRegistry elle-même est livrée ; seule cette interprétation de parsing de forme est réservée.
  • Baker les valeurs de marqueur personnalisé dans le code généré — hors périmètre tant qu'aucun consommateur n'a besoin des métadonnées de marqueur comme constantes/attributs de codegen.
  • Conteneurs SO par enregistrement / à chargement paresseux — conception complète, non construits ; le codegen actuel ne produit que des SO Database à chargement complet par onglet.

8. Évolution de schéma

  • Export/Push requièrent un bake frais après un changement de schéma — un bake périmé échoue avec ExportSchemaMismatch (empreintes non concordantes). Le refus propose désormais de lancer cet import à votre place : une seule confirmation le démarre, et rien n'est ensuite exporté ou poussé automatiquement — appuyez de nouveau sur l'action d'origine une fois l'import arrivé à terme.
  • Le premier import après un changement de schéma se déroule en interne en deux étapes (codegen → compilation → bake) — automatique, une seule action utilisateur ; seul un échec de compilation l'arrête (interruption sûre, phrase exploitable, plafond de 3 tentatives).

9. Périmètre de la localisation

Les fragments de détail du rapport (valeurs fautives, suggestions), les exceptions de bas niveau, et les logs de développeur sont en anglais, en ligne, à l'intérieur du squelette de rapport localisé — les interpolations à l'exécution ne peuvent pas être des clés de table de langue (limite standard de l'industrie). Tout ce qui est dessiné par l'éditeur, plus le squelette du rapport et les phrases pourquoi/comment, est entièrement localisé dans les 10 langues.

Les dix langues sont traduites intégralement. Chaque clé de chaque table de langue porte une vraie traduction — Data Studio, les préférences de thème, les dialogues et les lignes de log inclus. La parité des clés entre les dix fichiers est imposée par les tests, si bien que rien ne retombe sur une clé brute ni ne casse un placeholder.

Une poignée d'entrées par langue se lisent exactement comme la version anglaise, et c'est une décision de traduction plutôt qu'une passe en attente : ce sont des symboles et des chaînes qui ne sont que des placeholders (, +), des noms propres et des noms de format (Google Sheets, SHA-256), et des mots qu'une langue orthographie réellement comme l'anglais (OK, Alpha).

Les propres libellés d'un plugin ne sont pas du tout dans la table du Core : enregistrez-les avec ISheetForgeStringsPlugin pour qu'ils suivent la langue de l'utilisateur, ou laissez-les non enregistrés et ils s'affichent verbatim.

10. Interactions avec l'éditeur

  • Portée de Ctrl+Z : un champ de texte ayant le focus consomme Ctrl+Z en premier (standard du système d'exploitation) ; après une répercussion réussie, l'historique de préparation est effacé — l'annulation n'atteint jamais ce qui a déjà été écrit dans la feuille (la feuille est canonique).
  • Le changement de langue est verrouillé pendant Import/Export/Push (il déclenche une recompilation de régénération de menu). Les changements de thème ne sont pas verrouillés — ils ne déclenchent jamais de recompilation, si bien que la luminosité et le préréglage peuvent être changés à tout moment, y compris en cours d'exécution.
  • Le thème et la langue sont par utilisateur (EditorPrefs), pas par projet — chaque coéquipier garde le sien, et ni l'un ni l'autre n'apparaît dans le contrôle de version. Choisir un préréglage de couleur non par défaut écrit une feuille de style générée sous Assets/SheetForge/Editor/Generated/ (gitignorée, autoréparatrice) ; le préréglage par défaut n'écrit rien et la supprime.
  • Le code généré + les SO bake + le groupe Addressables sont des caches propres à chaque machine, gitignorés — chaque machine exécute un import une fois ; le code de jeu charge par adresse, jamais par référence de scène directe.

11. Licence

Le dépôt inclut un avis LICENSE : l'EULA de l'Unity Asset Store est l'accord qui fait foi, avec un avis de consultation du dépôt (code source visible à titre de référence et pour les acheteurs sous licence ; aucune redistribution/revente en dehors de l'EULA sans autorisation écrite). Code tiers : aucun — y compris le lecteur/écrivain OOXML xlsx écrit à la main.

12. État vérifié (à la sortie)

  • Double harnais de test : 2,150 tests .NET headless (2,150 réussis) + 3,021 tests EditMode (3,021 réussis, 0 échec, 4 sautés). Ces deux nombres sont l'unique endroit où les comptes sont énoncés ; toute autre page renvoie ici.
  • Les quatre sauts sont l'aller-retour Google en direct, qui ne s'exécute que lorsque des identifiants de compte de service sont présents dans l'environnement, et a été sauté dans cette exécution. Avec des identifiants, il a été exercé à plusieurs reprises contre une feuille de calcul réelle — récupération → push, y compris la détection de conflit ligne-déplacée, le traitement des flottants indépendant de la locale, un lot mixte inter-onglets, et une exécution complète du dispatcher affirmant un rapport fusionné unique et exactement un réimport automatique par envoi. Résultat : 4/4 au vert.
  • L'application web a ses propres barrières, toutes au vert : vérification de type, lint, 414 tests unitaires, une publication WebAssembly + exécution de fumée (qui charge une véritable DLL de plugin), un build de production, 56 tests de bout en bout en navigateur, et une vérification de constantes inter-langages (six paires Unity↔web : version d'hôte, format de plugin, version de schéma de registre, portée OAuth, noms d'assembly hôte, seuils de fenêtre de ligne).
  • Tous les asmdefs du produit compilés : 0 erreur, 0 avertissement. (Les asmdefs des exemples — deux dans chaque démo — n'arrivent que lorsque vous importez un package de démo, et ne sont pas compilés avant cela.)
  • Tests de garde au vert : zéro littéral coréen dans le code source du produit, zéro vocabulaire de domaine dans les coutures du noyau (les deux ignorent /Samples~/ — l'exemple est du contenu de domaine), parité des clés sur les 10 langues.
  • L'assembly de simulation de consommateur sans IVT compile contre la seule API publique (imposé par le compilateur). Une sonde de mini-plugin sans IVT qui s'y trouve implémente quinze des seize contrats d'extension (ISheetForgePlugin / validateur / arête / marqueur / modèle / graphe / registre de code / thème / UI Studio / chaînes / pipeline / ISheetSourceProvider / widget Studio / action d'inspecteur Studio / éditeur de cellule Studio) en utilisant la seule surface publique, si bien que la publicité des contrats reste prouvée même si l'exemple Plugin Demo n'est pas compilé. Le seizième, IStudioPanelProvider, renvoie un VisualElement et est exercé par un test côté éditeur à la place. La même sonde implémente aussi les interfaces de capacité optionnelles, y compris IReferencingCellType / IRefBearingValue, et les exerce à travers la surface publique (découverte par cast, les cinq hooks, préservation du reste).

Les éléments qui ne peuvent être vérifiés qu'au moment de la soumission à l'Asset Store (pas dans le dépôt) : le comportement de l'invite d'installation du .unitypackage, la déclaration de dépendance du Portal, l'exclusion des assemblies de test du package distribué, et une nouvelle vérification à 0 avertissement sur un projet propre.

13. Feuilles de localisation (texte de jeu)

Les limites des Feuilles de localisation et du pont Unity Localization, énoncées honnêtement. (Le package lui-même est optionnel — voir §1.)

Le pont est à sens unique, et les modifications de table externes font l'objet d'une question — jamais fusionnées

  • Quoi : la synchronisation va uniquement feuille → StringTables. Le pont estampille les tables qu'il possède et prend une empreinte de chaque synchronisation ; une table modifiée par autre chose depuis lors — la fenêtre Localization Tables, la propre extension Google Sheets d'Unity, un import XLIFF — fait que la synchronisation suivante s'arrête et demande : écraser depuis la feuille, ou abandonner avec un rapport de différences.
  • Pourquoi : deux postes de pilotage inscriptibles sur les mêmes données finissent en écrasements silencieux. La feuille est canonique, donc l'autre poste doit être explicite, pas silencieux.
  • Solution de contournement : faites transiter les traductions par la feuille — le classeur de traduction (export xlsx + réimport partiel des seules locales) existe précisément pour cela.

Une clé renommée en dehors du Studio équivaut à une suppression plus un ajout

  • Quoi : renommer une clé dans le Data Studio renomme l'entrée de table sur place, en préservant l'id interne auquel se lient les références LocalizedString — les références de scène survivent. Renommer la clé directement dans la source de la feuille (Google Sheets, Excel) est indiscernable de la suppression d'une clé et de l'ajout d'une autre : le pont crée une entrée neuve et l'ancienne devient orpheline, les références de scène continuant de pointer vers l'orpheline.
  • Pourquoi : un diff au niveau du texte ne peut pas distinguer un renommage d'une suppression-plus-ajout sans deviner, et une mauvaise supposition relierait silencieusement les références.
  • Solution de contournement : renommez les clés dans le Data Studio (l'un ou l'autre hôte) ; le rapport d'orphelines rattrape les suites d'un renommage externe.

Les clés orphelines de table sont préservées par défaut

  • Quoi : une clé présente dans la table mais plus dans la feuille est conservée, signalée comme orpheline, et retirée uniquement via l'action de nettoyage explicite — ou automatiquement, si vous activez le réglage supprimer à la synchronisation. Rien n'est jamais supprimé comme effet de bord.
  • Pourquoi : une ligne de feuille manquante peut être une erreur en cours d'édition ; détruire des traductions pour cela serait irrécupérable.

Uniquement les tables de chaînes — les tables d'assets ne sont pas couvertes

  • Quoi : le pont remplit des collections StringTable. L'axe AssetTable d'Unity Localization (sprites, audio, prefabs localisés) n'est pas synchronisé depuis les feuilles. Un élément reconnu du backlog.
  • Solution de contournement : gérez les tables d'assets avec les propres outils du package ; le pont n'y touche pas.

XLIFF et les pseudo-locales ne sont pas réimplémentés

  • Quoi : les tables synchronisées sont des tables Unity Localization ordinaires, si bien que l'export/import XLIFF et la pseudo-localisation propres au package fonctionnent dessus sans changement. SheetForge n'ajoute aucune seconde implémentation.
  • Frontière : la sortie que ces outils écrivent dans les tables compte comme une modification externe (première entrée ci-dessus). Gardez la feuille comme source de vérité et faites transiter les traductions par le classeur de traduction.

Aucun assistant Smart Format spécifique à une langue n'est livré

  • Quoi : la colonne smart marque une entrée comme Smart String, mais SheetForge ne fournit aucun formateur grammatical propre — la sélection des particules coréennes, par exemple, n'est pas incluse, délibérément.
  • Solution de contournement : les points d'extension Smart Format du package restent pleinement disponibles pour les formateurs que vous écrivez.

Les constantes de clé sont assainies en ASCII

  • Quoi : les constantes {Tab}Keys générées transforment chaque caractère hors des lettres ASCII, des chiffres et de _ en _ (les collisions reçoivent un suffixe numérique), si bien qu'une clé non-ASCII produit un nom de constante illisible — la clé elle-même fonctionne toujours partout.
  • Solution de contournement : gardez les clés en ASCII (ui.ok, dialog.intro) si vous utilisez les constantes. Comme le fichier d'enum défini par feuille, le fichier de constantes se génère toujours dans le dossier des paramètres — la même note d'assembly s'applique.

L'application web rédige les feuilles de localisation ; la synchronisation est celle de l'éditeur

  • Quoi : la création, la validation, la couverture, la génération de clé, la lentille de locale et le classeur de traduction fonctionnent tous dans le navigateur. Écrire les StringTables non — le navigateur n'a aucun projet Unity où écrire.
  • Pourquoi : un périmètre honnête, pas une fonctionnalité manquante : les tables vivent dans le projet.

Pages associées