diff --git a/README.md b/README.md index b87991b..8bd77e7 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,9 @@ versions publiées. La mise à jour automatique d'Android fonctionne à partir d - *Aviation* : 36 pictogrammes fournis, classés par groupes (train et commandes de vol, feux, pilote automatique, systèmes, radios et divers) ; - *Image* : vos propres images, rangées dans la **bibliothèque d'icônes**. + Cet onglet propose aussi des visuels fournis : interrupteurs à positions, et **point de vue** + (vue cockpit, vue extérieure, déplacements du siège Haut / Bas / Gauche / Droite / Avant / + Arrière en trois styles). La bibliothèque est un dossier du PC (`icons/` dans le dossier des données, un sous-dossier par thème : `icons/mon-avion/train.png`), servi aux Decks à `/user-icons/…`. Menu du profil → **Bibliothèque d'icônes** : ajout (glisser-déposer, plusieurs images à la fois), dossiers, diff --git a/package-lock.json b/package-lock.json index cee8ba3..0112806 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "streamsim", - "version": "0.12.15", + "version": "0.12.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "streamsim", - "version": "0.12.15", + "version": "0.12.16", "license": "MIT", "dependencies": { "electron-updater": "^6.8.9", diff --git a/package.json b/package.json index e8653c3..f9e27fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "streamsim", "productName": "StreamSim", - "version": "0.12.15", + "version": "0.12.16", "description": "Clone logiciel de Stream Deck : serveur PC, application de configuration et client Android.", "author": "StreamSim", "license": "MIT", diff --git a/public/docs.html b/public/docs.html index ce34d00..cfda470 100644 --- a/public/docs.html +++ b/public/docs.html @@ -149,6 +149,7 @@

Icônes et images

  • Emoji : une liste, ou un caractère tapé au clavier.
  • Aviation : 36 pictogrammes fournis avec l'application, classés par groupes (train et commandes de vol, feux, pilote automatique, systèmes, radios et divers). Ils sont blancs et prennent la couleur de la touche.
  • Image : vos propres images (PNG, JPG, WebP, GIF, SVG) en couleurs, rangées dans la bibliothèque d'icônes.
  • +
  • Dans l'onglet Image, des visuels fournis prêts à poser : les interrupteurs à positions et le point de vue (vue cockpit, vue extérieure, et les déplacements du siège Haut, Bas, Gauche, Droite, Avant, Arrière en trois styles : croix de flèches, pilote, fauteuil). Ce sont des touches entières, avec leur nom dessiné : décochez « Afficher le titre » pour ne pas le doubler.
  • La bibliothèque d'icônes

    C'est un dossier sur le PC : icons/ dans le dossier des données de StreamSim (%APPDATA%\StreamSim\data\icons sous Windows), avec un sous-dossier par thème, par exemple icons/mon-avion/train.png. Les Decks les affichent en les demandant au serveur (/user-icons/…). Une même image sert à autant de touches que vous voulez.

    diff --git a/public/icons/README.md b/public/icons/README.md index 069924d..c0cca93 100644 --- a/public/icons/README.md +++ b/public/icons/README.md @@ -12,6 +12,9 @@ public/icons/ ├── interrupteurs/ visuels d'interrupteurs à positions │ ├── levier-de-cote/ bascule-de-face/ glissiere/ haut, milieu, bas │ └── selecteur-8-positions/ 1 à 8 + ├── point-de-vue/ vues et déplacements du siège (choix d'icône → Image) + │ ├── vues/ cockpit, exterieure + │ └── siege-croix/ siege-pilote/ siege-fauteuil/ haut, bas, gauche, droite, avant, arriere └── rafale/ ├── selecteur-5k/ off, test, stby, norm, l, r ├── apu/ ecs/ off, on @@ -21,9 +24,9 @@ public/icons/ - **aviation/** : SVG 64 × 64, traits blancs de 4 (`stroke="#fff"`), extrémités arrondies, sans texte. Ajouter une icône : déposer le fichier dans le bon dossier puis la déclarer dans - `shared/icons.js` (`AVIATION_ICON_GROUPS`) pour qu'elle apparaisse dans le choix d'icône. + `shared/icons.js` (`AVIATION_ICON_GROUPS`) pour qu'elle apparaisse dans le choix d'icône. Les visuels du point de vue se déclarent de même dans `VIEW_FACE_GROUPS`. - **touches-completes/** : SVG 144 × 144 affichés en plein cadre à la place de la touche. - Un dossier par avion, un sous-dossier par commande, un fichier par état ou position. + Un dossier par thème (avion, point de vue…), un sous-dossier par commande, un fichier par état ou position. Ces icônes sont **dans l'application** et mises à jour avec elle. Vos propres images ne vont pas ici : elles sont rangées dans la **bibliothèque d'icônes** (dossier `icons/` des données de StreamSim, diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/arriere.svg b/public/icons/touches-completes/point-de-vue/siege-croix/arriere.svg new file mode 100644 index 0000000..44553f5 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/arriere.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +ARRIÈRE + diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/avant.svg b/public/icons/touches-completes/point-de-vue/siege-croix/avant.svg new file mode 100644 index 0000000..fadff95 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/avant.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +AVANT + diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/bas.svg b/public/icons/touches-completes/point-de-vue/siege-croix/bas.svg new file mode 100644 index 0000000..4b38a48 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/bas.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +BAS + diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/droite.svg b/public/icons/touches-completes/point-de-vue/siege-croix/droite.svg new file mode 100644 index 0000000..026c382 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/droite.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +DROITE + diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/gauche.svg b/public/icons/touches-completes/point-de-vue/siege-croix/gauche.svg new file mode 100644 index 0000000..401167e --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/gauche.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +GAUCHE + diff --git a/public/icons/touches-completes/point-de-vue/siege-croix/haut.svg b/public/icons/touches-completes/point-de-vue/siege-croix/haut.svg new file mode 100644 index 0000000..85f86d3 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-croix/haut.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +HAUT + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/arriere.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/arriere.svg new file mode 100644 index 0000000..a2aa46d --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/arriere.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +ARRIÈRE + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/avant.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/avant.svg new file mode 100644 index 0000000..006f66f --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/avant.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +AVANT + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/bas.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/bas.svg new file mode 100644 index 0000000..89fc00c --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/bas.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +BAS + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/droite.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/droite.svg new file mode 100644 index 0000000..386f4f1 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/droite.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +DROITE + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/gauche.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/gauche.svg new file mode 100644 index 0000000..c107338 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/gauche.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +GAUCHE + diff --git a/public/icons/touches-completes/point-de-vue/siege-fauteuil/haut.svg b/public/icons/touches-completes/point-de-vue/siege-fauteuil/haut.svg new file mode 100644 index 0000000..dc6f3e4 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-fauteuil/haut.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +HAUT + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/arriere.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/arriere.svg new file mode 100644 index 0000000..0e1ec58 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/arriere.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +ARRIÈRE + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/avant.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/avant.svg new file mode 100644 index 0000000..52840be --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/avant.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +AVANT + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/bas.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/bas.svg new file mode 100644 index 0000000..e2ef85c --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/bas.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +BAS + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/droite.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/droite.svg new file mode 100644 index 0000000..f3e1566 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/droite.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +DROITE + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/gauche.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/gauche.svg new file mode 100644 index 0000000..d4fd89a --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/gauche.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +GAUCHE + diff --git a/public/icons/touches-completes/point-de-vue/siege-pilote/haut.svg b/public/icons/touches-completes/point-de-vue/siege-pilote/haut.svg new file mode 100644 index 0000000..101fcdd --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/siege-pilote/haut.svg @@ -0,0 +1,11 @@ + + + + + + +SIÈGE + + +HAUT + diff --git a/public/icons/touches-completes/point-de-vue/vues/cockpit.svg b/public/icons/touches-completes/point-de-vue/vues/cockpit.svg new file mode 100644 index 0000000..0cde09a --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/vues/cockpit.svg @@ -0,0 +1,40 @@ + + + + + + + + +VUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +COCKPIT + diff --git a/public/icons/touches-completes/point-de-vue/vues/exterieure.svg b/public/icons/touches-completes/point-de-vue/vues/exterieure.svg new file mode 100644 index 0000000..eb21880 --- /dev/null +++ b/public/icons/touches-completes/point-de-vue/vues/exterieure.svg @@ -0,0 +1,30 @@ + + + + + + + + + +VUE + + + + + + + + + + + + + + + + + + +EXTÉRIEUR + diff --git a/public/js/app.js b/public/js/app.js index 835b5d5..2357b7e 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -8,7 +8,7 @@ import { import { computeCells, placementError, findFreeSlot, keySpan, stateKey } from '/shared/layout.js'; import { SIMHUB_PROPERTIES } from '/shared/simhub.js'; import { switchCount } from '/shared/controls.js'; -import { AVIATION_ICON_GROUPS, aviationIcon, SWITCH_FACE_GROUPS, switchFaces } from '/shared/icons.js'; +import { AVIATION_ICON_GROUPS, aviationIcon, SWITCH_FACE_GROUPS, switchFaces, VIEW_FACE_GROUPS, viewFaces } from '/shared/icons.js'; import { library, refreshIcons, uploadIconFile, openIconLibrary, exportIcons, pickAndImportIcons } from './icons.js'; import { MSFS_EVENTS, MSFS_EVENT_LABELS, MSFS_SIMVARS, MSFS_NUMERIC_SIMVARS, MSFS_UNITS, FBW_EVENTS, FBW_PRESETS, isLocalVar } from '/shared/msfs.js'; @@ -2015,6 +2015,17 @@ function appearanceSection(i) { ), ), ]), + h('div', { class: 'icon-group-title' }, 'Point de vue : vues et siège (fournis)'), + ...VIEW_FACE_GROUPS.flatMap((g) => [ + h('p', { class: 'hint' }, g.label), + h( + 'div', + { class: 'emoji-grid user-icon-grid' }, + ...viewFaces(g).map((src, i) => + h('button', { class: key.icon === src ? 'on' : '', title: `${g.label} : ${g.files[i][1]}`, onclick: () => setFace({ icon: src }, { render: 'all' }) }, h('img', { src, alt: g.files[i][1], draggable: 'false' })), + ), + ), + ]), h('div', { class: 'icon-group-title' }, library.icons.length ? `Ma bibliothèque (${library.icons.length})` : 'Ma bibliothèque'), recent.length ? h( diff --git a/shared/icons.js b/shared/icons.js index 00dd88b..bd53ab5 100644 --- a/shared/icons.js +++ b/shared/icons.js @@ -39,6 +39,18 @@ export const SWITCH_FACE_GROUPS = [ /** Chemins des visuels d'un groupe, dans l'ordre des positions. */ export const switchFaces = (g) => g.positions.map((n) => `${SWITCH_FACES}/${g.folder}/${n}.svg`); +/** Visuels du point de vue : vues cockpit / extérieure et déplacements du siège (haut, bas, gauche…). */ +export const VIEW_FACES = '/public/icons/touches-completes/point-de-vue'; +const SEAT_MOVES = [['haut', 'Haut'], ['bas', 'Bas'], ['gauche', 'Gauche'], ['droite', 'Droite'], ['avant', 'Avant'], ['arriere', 'Arrière']]; +export const VIEW_FACE_GROUPS = [ + { folder: 'vues', label: 'Vues : cockpit et extérieure', files: [['cockpit', 'Cockpit'], ['exterieure', 'Extérieure']] }, + { folder: 'siege-croix', label: 'Siège : croix de flèches', files: SEAT_MOVES }, + { folder: 'siege-pilote', label: 'Siège : pilote', files: SEAT_MOVES }, + { folder: 'siege-fauteuil', label: 'Siège : fauteuil', files: SEAT_MOVES }, +]; +/** Chemins des visuels d'un groupe, dans l'ordre. */ +export const viewFaces = (g) => g.files.map(([n]) => `${VIEW_FACES}/${g.folder}/${n}.svg`); + /** Visuel entier d'une touche : /public/icons/touches-completes/... (affiché en plein cadre). */ export const isFacePath = (icon) => typeof icon === 'string' && /^\/public\/icons\/touches-completes\/[\w/-]+\.svg$/.test(icon); diff --git a/test/icons.test.js b/test/icons.test.js index 172e92b..1e25084 100644 --- a/test/icons.test.js +++ b/test/icons.test.js @@ -152,3 +152,20 @@ test('icônes intégrées : visuels des interrupteurs à positions présents', a } } }); + +test('icônes du point de vue : vues et siège, chaque visuel listé existe et le dossier ne contient rien d’autre', async () => { + const { VIEW_FACE_GROUPS, VIEW_FACES, viewFaces, isFacePath } = await import('../shared/icons.js'); + const root = path.join(ROOT, 'public/icons/touches-completes/point-de-vue'); + assert.deepEqual((await fs.readdir(root)).sort(), VIEW_FACE_GROUPS.map((g) => g.folder).sort()); + for (const g of VIEW_FACE_GROUPS) { + assert.deepEqual((await fs.readdir(path.join(root, g.folder))).sort(), g.files.map(([n]) => `${n}.svg`).sort(), g.folder); + for (const f of viewFaces(g)) { + assert.ok(isFacePath(f) && f.startsWith(VIEW_FACES), f); + const svg = await fs.readFile(path.join(ROOT, f), 'utf8'); + assert.match(svg, /^]+viewBox="0 0 144 144"/); + assert.doesNotMatch(svg, / g.folder === 'siege-pilote').files.length, 6); + assert.equal(VIEW_FACE_GROUPS.find((g) => g.folder === 'vues').files.length, 2); +});