Aller au contenu
SheetForge

SheetForge Web

SheetForge livre une application web compagne sur web.sheetforge.workers.dev. Elle apporte la création, la validation et la réflexion de feuille au navigateur — sans Unity, sans installation.

Le même cœur, pas une réimplémentation

L'application web compile les mêmes sources C# du cœur, à l'octet près — celles que le harnais de tests .NET headless exécute — en un assembly .NET browser-wasm. Il n'existe aucune seconde copie du parseur ou du validateur, si bien que les deux ne peuvent jamais diverger : une règle corrigée une fois est corrigée aux deux endroits.

Deux choses découlent directement de cette conception.

  • Vos DLL de plugin compilées se chargent sans modification, et elles allument les mêmes emplacements ici que dans l'éditeur (ci-dessous).
  • Les règles de syntaxe de feuille de ce site s'appliquent à l'identique dans le navigateur. Les marqueurs, le système de types, @overlap, @style, @enum, @loc et l'intégrité des références se comportent tous de la même façon, car c'est le même code qui répond. Cela inclut la parité IntId@Tab et LocRef@Tab — voir Syntaxe des feuilles.

Un plugin, douze emplacements, deux hôtes

Un plugin n'a pas de « sous-ensemble web ». L'assemblage — instanciation, ordonnancement, isolation et barrière de compatibilité — est une seule fonction Core pure que les deux hôtes appellent. Seule la découverte diffère : Unity indexe les types du projet, tandis que le navigateur scanne l'assembly que vous avez téléversé.

Les douze emplacements qu'un plugin peut remplir sont donc les mêmes des deux côtés :

EmplacementDans le navigateur
Enums · parseurs de cellule · types wrapperLes cellules s'analysent, se valident et font l'aller-retour via votre propre notation
Validateurs de domaineVos règles apparaissent dans Problems à côté de celles du Core
Contributeurs d'arêtesLes liens enfouis dans votre notation sont dessinés sur le canevas et comptés dans l'index de référence
Marqueurs structurelsVos lignes @marker sont acceptées et validées
Modèles de feuilleVos modèles apparaissent dans Create sheet
Augmenteurs de canevasNœuds virtuels, arêtes supplémentaires, indices de calque et d'affichage
Registres de codeLes clés qui vivent dans le code cessent d'être dessinées comme des références cassées
Préréglages de couleurMappés sur les variables CSS de l'application
UI Studio déclarativeVos actions, panneaux, badges de colonne et indices d'éditeur de cellule, rendus en React
Chaînes d'UIVos libellés suivent la langue de l'utilisateur via la même surcouche que consulte t()
Observateurs de pipelineNotifiés à la fin d'un cycle d'import, exactement comme dans l'éditeur

Les surfaces déclaratives sont la raison pour laquelle les extensions de création d'un plugin existent ici tout court. Un contrat qui rendait un élément UIToolkit ne pourrait jamais se charger dans un navigateur, donc la coquille est décrite comme des données — un id, une clé de libellé, un emplacement, un ton — et chaque hôte la dessine avec ses propres widgets.

Là où un pack a besoin de quelque chose que le vocabulaire ne peut pas exprimer, il enregistre un panneau riche réservé à l'éditeur sous le même id. L'éditeur dessine celui-là, le navigateur dessine celui décrit, et l'extension n'est jamais simplement absente.

Deux règles tiennent au moment du rendu. Le texte provenant d'un plugin est échappé, et une action dont la situation a changé depuis qu'elle a été dessinée répond par un no-op honnête plutôt que d'agir sur un id périmé.

Un assembly de plugin peut aussi déclarer pour quelle génération du format de plugin il a été compilé. Si cela tombe hors de ce que cet hôte lit, l'assembly entier est refusé avec une raison lisible plutôt que chargé à moitié. C'est la même barrière, lisant la même déclaration, sur le chemin du marché, le chemin de sideload et un fichier local. Voir Création de plugins.

Ce qui reste dans Unity

Le codegen et le bake restent une responsabilité réservée à Unity. Le navigateur ne peut pas émettre de fichiers .cs ni écrire de ScriptableObjects, et il ne prétend pas le faire. La même honnêteté couvre la localisation : une feuille de localisation est entièrement rédigée et validée ici, mais synchroniser les StringTables Unity Localization est le travail de l'éditeur — le navigateur n'a aucun projet Unity où écrire les tables. Il en va de même pour tout ce qui a besoin du projet lui-même : le sélecteur d'asset, le glisser-déposer depuis la fenêtre Project et les enregistrements Addressables qu'ils préparent n'existent que dans l'éditeur ; le <Type> d'AssetRef@Group<Type> est analysé et montré dans le navigateur mais résolu — et vérifié par type — seulement là où les types du projet sont chargés ; et les vignettes, aperçus agrandis et lecture audio d'asset de l'éditeur n'ont pas d'équivalent web, car le navigateur n'a aucun asset de projet à lire — une cellule d'asset dans la grille web est son texte d'adresse.

La sortie du web est la feuille réfléchie — la source validée et ayant fait l'aller-retour — pas des assets bakés. Vous créez et validez dans le navigateur, puis lancez un import dans Unity quand vous voulez des classes fortement typées et des SO bake. La feuille est la source unique de vérité dans tous les cas, si bien que les deux surfaces se rejoignent au niveau de la feuille.

Ce que fait l'application web

Tout fait l'aller-retour à travers un unique contrat de frontière JSON vers le cœur WebAssembly, si bien que l'UI du navigateur ne redérive jamais une règle que le cœur possède déjà.

ZoneCe que vous obtenez
Session de créationImport, projection, et un panneau Problems alimenté par le même validateur d'import — des diagnostics de validation préalable, pas une vérification séparée. L'import accepte les fichiers TSV et CSV ainsi que les classeurs .xlsx — chaque feuille d'un classeur arrive comme son propre onglet, lue par le même lecteur qu'utilise l'éditeur (compilé en WebAssembly), et quand quelque chose a dû être interprété — la valeur mise en cache d'une formule, une date en texte yyyy-MM-dd, une mise en forme non lue — la boîte de dialogue d'import le dit dans une note « Comment ce classeur a été lu ».
Édition de tableWidgets de cellule par type, annulation, épinglage de colonne, tri, recherche, regroupement et couleur @style — plus l'ensemble de colonnes façon tableur qu'a l'éditeur : faites glisser la poignée de l'en-tête pour définir la largeur d'une colonne (double-clic pour ajuster ; enregistré par feuille dans ce navigateur), masquez et affichez des colonnes tout en gardant les lettres et une marque de délimitation honnêtes, des filtres de valeur par colonne qui se combinent avec la recherche, et l'effacement des données de colonne comme une seule étape d'annulation. + Row prépare aussitôt une ligne avec une clé suggérée — les colonnes d'id entier étant pré-remplies — prête à être renommée dans la cellule de clé, et une feuille sans colonne clé le désactive, avec la raison indiquée. Les cellules d'enum et de booléen optionnel ouvrent le même sélecteur cherchable qu'utilisent les cellules de référence — et une seule fenêtre de sélecteur est jamais ouverte à la fois : en ouvrir une autre ferme la précédente, et cliquer ailleurs ou Esc la ferme. Les numéros de ligne et les lettres de colonne se sélectionnent (Ctrl ajoute, Shift étend la portée), l'en-tête et le corps mettent en surbrillance la sélection, la cellule sélectionnée dessine un contour qui suit le clavier, et une sélection se déplace vers une nouvelle position par glisser avec une ligne d'insertion — une sélection non adjacente arrive regroupée au point de dépôt dans l'ordre où elle a été sélectionnée, comme une seule étape d'annulation — tandis que les menus de clic droit appliquent largeur, ajustement, masquage, déplacement, suppression et effacement des données à chaque colonne sélectionnée (déplacements et suppression à chaque ligne sélectionnée), les éléments à cible unique se tenant à part dans leur propre section. Les feuilles de définition d'enum ont le même axe de largeur de colonne que les feuilles de données — glisser, double-clic pour ajuster, largeur exacte, ajuster à la donnée, masquer. Au-delà de 200 lignes, la grille ne rend que la fenêtre visible (plus une marge de balayage), avec des espaceurs portant la vraie hauteur de défilement — le même seuil qu'utilise la table de l'éditeur, si bien que les deux se ressentent de façon identique sur une grande feuille.
Valeurs visuellesLes cellules Color, AnimationCurve et Gradient montrent un aperçu qui remplit la cellule — un échantillon de couleur, une polyligne de courbe, une barre de dégradé — et ouvrent un éditeur complet dans une popover : un éditeur de couleur avec un carré HSV, des curseurs de teinte et d'alpha et un champ hexadécimal (le champ de couleur natif du navigateur n'a pas d'alpha, il n'est donc pas utilisé) ; un éditeur de courbe avec une grille zoomable, des clés et des poignées de tangente à glisser, des modes de tangente (Free, Auto, Linear, Constant, ClampedAuto — clic droit sur une clé ou utilisez la sélection), des bascules rompue et pondérée, des champs numériques, un wrap pré/post et les préréglages Linéaire / Entrée-sortie douce / Constante ; un éditeur de dégradé avec des clés de couleur et d'alpha à glisser (jusqu'à 8 de chaque), une sélection de mode et une sélection d'espace colorimétrique. Chaque changement fait l'aller-retour par le cœur WebAssembly — l'analyse, le recalcul de tangente et l'échantillonnage sont en C#, pas en JavaScript — et le texte validé est la même forme canonique que celle qu'écrit l'éditeur, à l'octet près. Les changements se valident à l'instant où vous les faites, exactement comme les champs natifs de l'éditeur : la cellule, le canevas et Problems suivent pendant que l'éditeur est ouvert, une session d'édition fusionne en une seule étape d'annulation quel que soit le nombre de changements qu'elle fait, et rouvrir l'éditeur démarre une nouvelle étape. Il n'y a pas de bouton Appliquer — l'unique bouton est Fermer (Esc ferme aussi, et dans l'éditeur de couleur Esc restaure aussi la valeur d'avant la session) ; revenir en arrière se fait par Ctrl+Z. Ouvrir puis fermer sans rien changer ne prépare rien. Une colonne optionnelle montre quand elle est vide et offre Effacer (valeur par défaut) dans un menu au clic droit ; un List<> des trois est une liste à puces dont les gestes ajouter / retirer / réordonner / éditer sont chacun une étape d'annulation. La bande d'aperçu tient dans la hauteur de ligne standard, comme dans la table de l'éditeur. Les lignes de valeur du canevas utilisent les mêmes composants.
Édition de structureAjouter / supprimer / déplacer des colonnes, le formulaire de colonne à quatre champs, renommage d'onglet (permutations mutuelles et cycles inclus ; les onglets xlsx sont bloqués), création/suppression de feuille, et une liste déroulante de paramètres actifs — chacun préparé comme une étape d'annulation. Les feuilles de définition d'enum se rédigent de la même façon — définir un nouvel enum, renommer (avec chaque @type qui le référence réécrit dans le même lot), supprimer, réorganiser les colonnes, éditer le type sous-jacent et la description — sous les mêmes règles que l'éditeur, car c'est le même code qui décide.
Recherche multi-feuilleL'entrée All de la barre latérale recherche par correspondance partielle dans les id et les valeurs de champ de toutes les feuilles, en listant feuille · clé · champ · valeur, 50 lignes par page — la même correspondance, le même ordre et la même taille de page que l'éditeur. Elle lit les valeurs que la session montre à l'instant, modifications préparées incluses, si bien qu'une valeur que vous venez de changer est trouvée aussitôt. Double-cliquez sur un résultat — ou sélectionnez-le et appuyez sur Entrée — et cette feuille s'ouvre avec la cellule correspondante sélectionnée ; puisque la recherche lit la session elle-même, chaque résultat peut être atteint.
LocalisationLes feuilles de localisation sont des feuilles ordinaires ici — création, validation et couverture par locale incluses. La cellule LocRef d'une feuille de données montre en ligne le texte de la locale source de l'entrée et ouvre le même sélecteur de clé que les autres références ; taper dans une cellule vide génère la clé, son texte source et la référence en une seule étape d'annulation, exactement comme le fait l'éditeur. La lentille de locale bascule les colonnes de locale visibles (affichage seul), et le classeur de traduction exporte les locales choisies en xlsx et réimporte le fichier renvoyé comme une fusion ne touchant que les colonnes de locale. Synchroniser les StringTables Unity Localization reste le travail de l'éditeur.
Canevas d'enregistrementsLes matériaux et consommateurs d'un enregistrement sont des cartes que vous connectez en faisant glisser des fils — le canevas réutilise le même code pur de disposition, de câblage et de chaîne d'édition que l'éditeur, si bien que la géométrie en pixels vit en C#, pas dans une bibliothèque de graphe JS. Les cartes montrent la même projection que la table : une colonne préparée dans ce lot se tient déjà sur la carte marquée comme en attente, un renommage préparé affiche le nouveau nom par-dessus la valeur d'origine, et une suppression préparée retire aussi la ligne ici.
SortieTéléchargement TSV / CSV par feuille, un téléchargement de classeur .xlsx portant chaque feuille choisie dans un seul fichier (un nom de feuille que les règles du classeur ne peuvent pas porter est ajusté et signalé, jamais renommé silencieusement), et lecture/écriture Google Sheets, avec les listes déroulantes de validation des données de la feuille actualisées automatiquement après chaque push (voir ci-dessous).

Rendu identique, pas seulement similaire

L'UI web correspond au Data Studio de l'éditeur volontairement, jusque dans la logique partagée.

  • Réponses partagées. Les prédicats d'en-tête (pending / edited), la fenêtre modale de diff, et l'ordre de composition de la projection sont du C# partagé, si bien que les deux surfaces donnent la même réponse à la même question.
  • Phrases partagées. Là où le web reflète un écran du Studio, il utilise les propres clés de chaîne du Studio, et les valeurs sont générées depuis les tables de langue de l'éditeur au moment du build plutôt que retapées. Ajouter une phrase que les deux surfaces montrent, c'est une seule clé dans Lang*.cs ; la dérive est structurellement impossible plutôt que simplement découragée. Deux choses sont délibérément tenues séparées, et un garde-fou au moment du build impose la frontière :
    • les chaînes véritablement propres au web restent dans le catalogue web ;
    • les diagnostics et les rapports sont rendus par le cœur WebAssembly, jamais reformulés dans l'UI.
  • Apparence partagée. Le graphe et la feuille sont montrés ensemble dans un panneau divisé redimensionnable qui reflète le comportement de bridage de canevas de l'éditeur, et chaque couleur provient de l'ensemble de tokens .aw-root de l'éditeur comme source unique de vérité — aucune palette codée en dur.
  • Kit de composants partagé. L'UI est construite sur un kit maison aw dont les noms de classe, les tokens et le placement des popups correspondent un-à-un à l'USS de l'éditeur et à la maquette HTML canonique. Une seule maquette alimente désormais deux ports — l'application web et l'éditeur UIToolkit — au lieu que le web dérive de son côté. web/Docs/ui-parity.md consigne comment le web suit le studio quand celui-ci change.
  • Éditeurs de cellule partagés. Quel widget reçoit une cellule est décidé par l'indice d'éditeur de cellule du cœur — l'enregistrement d'un pack d'abord, puis la table intégrée (BuiltinCellEditorHints) — si bien qu'un type de plugin qui déclare l'archétype ColorPicker, CurveEditor ou GradientEditor ouvre ici les mêmes éditeurs que ceux qu'il ouvre dans Unity, et les deux hôtes ne peuvent pas choisir des widgets différents pour le même nom de type.
  • Parité de canevas. Le glisser-déposer a atteint la pleine parité avec l'éditeur : déplacement (pan) au glissement du bouton central ou Alt-glissement, menus en cascade sur canevas vide, inspecteurs de fil et de nœud, effet fantôme du fil saisi, et suppression du terminus avec une boîte de dialogue de confirmation. La plage de zoom est celle de l'éditeur (25–200 %), et les métriques de carte proviennent du cœur plutôt que d'une seconde copie en JavaScript.
  • Liste de types partagée. La liste déroulante @type propose ce que le registre de parseurs connaît réellement, groupé exactement comme l'éditeur les groupe : les scalaires intégrés (Color, AnimationCurve et Gradient compris), les clés RecordId / IntId, les références RecordId@Tab et IntId@Tab, Enum<…>, AssetRef@Group, et tout type ou wrapper qu'un plugin chargé a enregistré. Un plugin qui enregistre un nouveau type apparaît donc dans le navigateur sans changement web. (Avant que le cœur WebAssembly ne termine son démarrage, le tout premier rendu retombe sur les quatre scalaires intégrés.)

Quand utiliser lequel

  • Utilisez le navigateur pour la création rapide, la validation et les modifications de feuille depuis n'importe où, et pour les coéquipiers qui n'ouvrent pas Unity.
  • Utilisez Unity quand vous avez besoin de classes C# générées et de ScriptableObjects bakés — les étapes d'import, de codegen et de bake.

Les deux écrivent dans la même feuille, si bien qu'un changement fait dans l'un est repris par l'autre à la prochaine lecture.

Pages associées