Compare commits

...

3 commits

Author SHA1 Message Date
isUnknown
5eaf99f7cd Feat: page Play — backgroundColor, playLink, play-icon + fix slug jouer
All checks were successful
Deploy / Deploy to Production (push) Successful in 5m22s
- Renommage dossier content 3_play → 3_jouer (slug /jouer)
- Champ backgroundColor (code-editor) dans game.yml + exposé en API
- playLinks simplifié en champ url simple → playLink dans l'API
- Variable CSS --background-color sur la section .play
- Bouton "Jouer" avec icône play-icon (SVG data URI dans buttons.css)
- Plugin sylvainjule/code-editor ajouté via composer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:41:05 +01:00
isUnknown
3cafcf3167 Feat: lignes courbes SVG sur Play + refonte blueprint game
- Play.svelte : ajout overlay SVG (background-play), suppression des vertical-line
- game.yml : restructuration du blueprint
- Header.svelte : modifications mineures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 14:18:38 +01:00
isUnknown
69859cc60f Refactor: snake_case → camelCase dans blueprints, templates et vues
- Blueprints : renommage des champs (member_name, related_articles,
  background_video, play_links, images_gallery, external_links) et
  des noms de sections
- Templates JSON PHP : clés de sortie et appels ->method() en camelCase
- Vues Svelte (Play, Portfolio) : accès aux données alignés

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:40:33 +01:00
42 changed files with 858 additions and 281 deletions

View file

@ -1,76 +1,81 @@
@font-face { @font-face {
font-family: 'Terminal Grotesque'; font-family: "Terminal Grotesque";
src: url('fonts/terminal-grotesque.ttf') format('truetype'); src: url("fonts/terminal-grotesque.ttf") format("truetype");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'Danzza'; font-family: "Danzza";
src: url('fonts/Danzza-Light.woff') format('woff'); src: url("fonts/Danzza-Light.woff") format("woff");
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'Danzza'; font-family: "Danzza";
src: url('fonts/Danzza-Regular.woff') format('woff'); src: url("fonts/Danzza-Regular.woff") format("woff");
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'Danzza'; font-family: "Danzza";
src: url('fonts/Danzza-Medium.woff') format('woff'); src: url("fonts/Danzza-Medium.woff") format("woff");
font-weight: 500; font-weight: 500;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'Danzza'; font-family: "Danzza";
src: url('fonts/Danzza-Bold.woff') format('woff'); src: url("fonts/Danzza-Bold.woff") format("woff");
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;
} }
:root{ :root {
--font-title: 'Terminal Grotesque', sans-serif; --font-title: "Terminal Grotesque", sans-serif;
--font-body: 'Danzza', sans-serif; --font-body: "Danzza", sans-serif;
--font-sans: 'Danzza', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; --font-sans: "Danzza", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
--text-h1: 3em; "Segoe UI Symbol";
--color-green-perso: #04fea0; --text-h1: 3em;
--color-black: #0B1632; --color-green-perso: #04fea0;
--color-white: #ffffff;
--color-gray-200: #BCCAF0; --color-black: #0b1632;
--color-gray-100: #BCCAF0; --color-white: #ffffff;
--color-gray-250: #8BA3E5;
--color-gray-300: #7A95E1;
--color-gray-400: #597BD9;
--color-gray-500: #3761D2;
--color-gray-600: #224196;
--color-gray-700: #1A3274;
--color-gray-800: #172C64;
--color-gray-850: #132553;
--color-gray-900: #0e1e43;
--color-gray-950: #0d1a38;
--button-cancel-filled-color-back: light-dark( var(--color-gray-300), var(--color-gray-950) ); --color-gray-200: #bccaf0;
--color-icon-perso: light-dark( var(--color-gray-700), var(--color-gray-400) ); --color-gray-100: #bccaf0;
--color-gray-250: #8ba3e5;
--color-gray-300: #7a95e1;
--color-gray-400: #597bd9;
--color-gray-500: #3761d2;
--color-gray-600: #224196;
--color-gray-700: #1a3274;
--color-gray-800: #172c64;
--color-gray-850: #132553;
--color-gray-900: #0e1e43;
--color-gray-950: #0d1a38;
--color-border: light-dark(var(--color-gray-300), var(--color-gray-600)); --button-cancel-filled-color-back: light-dark(
--dropdown-color-hr:var(--color-gray-800); var(--color-gray-300),
var(--color-gray-950)
);
--color-icon-perso: light-dark(var(--color-gray-700), var(--color-gray-400));
--color-focus: var(--color-green-perso); --color-border: light-dark(var(--color-gray-300), var(--color-gray-600));
--dropdown-color-hr: var(--color-gray-800);
--color-red-h: 355; --color-focus: var(--color-green-perso);
--color-green-h: 152;
--color-orange-h: 15; --color-red-h: 355;
--color-blue-h: 220; --color-green-h: 152;
--color-orange-h: 15;
--color-blue-h: 220;
} }
.k-dialog-button-cancel:where([data-variant="filled"]) { .k-dialog-button-cancel:where([data-variant="filled"]) {
@ -81,25 +86,27 @@
--icon-color: var(--color-icon-perso); --icon-color: var(--color-icon-perso);
} }
.k-panel-menu-button:hover:not(.k-activation-button):not(.k-panel-menu-search){ .k-panel-menu-button:hover:not(.k-activation-button):not(.k-panel-menu-search) {
color: var(--color-green-perso); color: var(--color-green-perso);
} }
.k-header-title {
.k-header-title{ font-family: var(--font-title);
font-family: var(--font-title);
} }
.k-panel[data-template="home"] .k-header-title-text, .k-panel[data-template="expertise"] .k-header-title-text, .k-panel[data-template="home"] .k-header-title-text,
.k-panel[data-template="portfolio"] .k-header-title-text, .k-panel[data-template="jouer"] .k-header-title-text, .k-panel[data-template="expertise"] .k-header-title-text,
.k-panel[data-template="about"] .k-header-title-text, .k-panel[data-template="blog"] .k-header-title-text{ .k-panel[data-template="portfolio"] .k-header-title-text,
text-transform: uppercase; .k-panel[data-template="jouer"] .k-header-title-text,
.k-panel[data-template="about"] .k-header-title-text,
.k-panel[data-template="blog"] .k-header-title-text {
text-transform: uppercase;
} }
.k-header-title .k-icon{ .k-header-title .k-icon {
--icon-size: 28px; --icon-size: 28px;
} }
.k-text a{ .k-text a {
color: var(--color-green-perso); color: var(--color-green-perso);
} }
.k-block-container:not(:last-of-type) { .k-block-container:not(:last-of-type) {
@ -107,35 +114,46 @@
} }
/*modif card pour ressembler au site web*/ /*modif card pour ressembler au site web*/
.k-panel[data-template="blog"] .k-item.k-cards-item[data-template="article"]{ .k-panel[data-template="blog"] .k-item.k-cards-item[data-template="article"] {
display: grid; display: grid;
grid-template-columns: 2fr auto 1fr; grid-template-columns: 2fr auto 1fr;
grid-template-rows: auto; grid-template-rows: auto;
} }
.k-panel[data-template="blog"] .k-item.k-cards-item[data-template="article"] .k-item-image{ .k-panel[data-template="blog"]
grid-column: 3/4; .k-item.k-cards-item[data-template="article"]
grid-row: 1/3; .k-item-image {
grid-column: 3/4;
grid-row: 1/3;
} }
.k-panel[data-template="blog"] .k-item.k-cards-item[data-template="article"] .k-item-content{ .k-panel[data-template="blog"]
grid-column: 1/2; .k-item.k-cards-item[data-template="article"]
grid-row: 1/2; .k-item-content {
grid-column: 1/2;
grid-row: 1/2;
} }
.k-panel[data-template="blog"] .k-item.k-cards-item[data-template="article"] .k-item-options{ .k-panel[data-template="blog"]
grid-column: 2/3; .k-item.k-cards-item[data-template="article"]
grid-row: 1/2; .k-item-options {
align-items: start; grid-column: 2/3;
grid-row: 1/2;
align-items: start;
} }
/* Logo sur la page de login */ /* Logo sur la page de login */
.k-login-view .k-login-form::before { .k-login-view .k-login-form::before {
content: ''; content: "";
display: block; display: block;
width: 150px; width: 150px;
height: 150px; height: 150px;
margin: 0 auto 2rem auto; margin: 0 auto 2rem auto;
background-image: url('logo-panel.png'); background-image: url("logo-panel.png");
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
} }
/* Code editor style */
.k-code-editor-input[data-size="custom-size"] {
min-height: 2rem;
}

View file

@ -22,7 +22,8 @@
}, },
"require": { "require": {
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"getkirby/cms": "^5.2" "getkirby/cms": "^5.2",
"sylvainjule/code-editor": "^1.1"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {

40
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "da1c3a8eb3e9e3b252ad405f32a3f585", "content-hash": "07c4d6a85de1d10e6c6fc0e5cec09033",
"packages": [ "packages": [
{ {
"name": "christian-riesen/base32", "name": "christian-riesen/base32",
@ -721,6 +721,44 @@
}, },
"time": "2024-09-11T13:17:53+00:00" "time": "2024-09-11T13:17:53+00:00"
}, },
{
"name": "sylvainjule/code-editor",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/sylvainjule/kirby-code-editor.git",
"reference": "adbc2c8a728994cc57ea72a7f8628f27d202b8df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sylvainjule/kirby-code-editor/zipball/adbc2c8a728994cc57ea72a7f8628f27d202b8df",
"reference": "adbc2c8a728994cc57ea72a7f8628f27d202b8df",
"shasum": ""
},
"require": {
"getkirby/composer-installer": "^1.2"
},
"type": "kirby-plugin",
"extra": {
"installer-name": "code-editor"
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sylvain Julé",
"email": "contact@sylvain-jule.fr"
}
],
"description": "Code editor field for Kirby 3, 4 and 5",
"support": {
"issues": "https://github.com/sylvainjule/kirby-code-editor/issues",
"source": "https://github.com/sylvainjule/kirby-code-editor/tree/1.1.0"
},
"time": "2025-08-04T17:32:08+00:00"
},
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
"version": "v3.6.0", "version": "v3.6.0",

View file

@ -2,7 +2,7 @@ title: Membre de l'équipe
accept: image/* accept: image/*
fields: fields:
member_name: memberName:
label: Nom label: Nom
type: text type: text
required: true required: true

View file

@ -78,5 +78,5 @@ tabs:
ratio: 3/5 ratio: 3/5
cover: true cover: true
back: '#0e1e43' back: '#0e1e43'
text: "{{ file.member_name }}" text: "{{ file.memberName }}"
info: "{{ file.role }}" info: "{{ file.role }}"

View file

@ -105,7 +105,7 @@ tabs:
related: related:
type: fields type: fields
fields: fields:
related_articles: relatedArticles:
label: Articles recommandés label: Articles recommandés
type: pages type: pages
query: site.find('blog').children.listed query: site.find('blog').children.listed

View file

@ -43,7 +43,7 @@ tabs:
cover: true cover: true
ratio: 16/9 ratio: 16/9
info: "{{ page.date.toDate('d/m/Y') }}" info: "{{ page.date.toDate('d/m/Y') }}"
articles_list: articlesList:
label: Articles label: Articles
type: pages type: pages
headline: Liste des articles headline: Liste des articles

View file

@ -1,98 +1,83 @@
title: Jeu title: Jeu
icon: play icon: play
# status: status:
# draft: draft:
# label: Brouillon label: Brouillon
# text: Le jeu n'est pas visible text: Le jeu n'est pas visible
# unlisted: unlisted:
# label: Non listé label: Non listé
# text: Le jeu est accessible mais pas dans la liste text: Le jeu est accessible mais pas dans la liste
# listed: listed:
# label: Publié label: Public
# text: Le jeu est visible publiquement text: Le jeu est visible publiquement
columns: columns:
# Colonne principale - width: 2/10
main: fields:
width: 2/3 thumbnail:
sections: label: Vignette
# Informations principales type: files
content: layout: cards
type: fields size: small
fields: max: 1
lettering: accept: image/*
label: Lettrage du jeu translate: false
type: files image:
layout: cardlets ratio: 1/1
size: small uploads:
max: 1 template: image
accept: image/* help: Pour le carousel de navigation parmi les jeux
translate: false - width: 2/10
image: fields:
ratio: 1/1 lettering:
uploads: label: Lettrage
template: image type: files
help: Image avec le lettrage du jeu multiple: false
description: translate: false
label: Description layout: cards
type: writer size: tiny
marks: uploads:
- bold template: image
- italic help: Affiché au-dessus de la description
- green width: 1/3
- pixel - width: 3/10
- underline fields:
- strike description:
- clear label: Description
- link type: writer
marks:
- bold
- italic
- green
- pixel
- underline
- strike
- clear
- link
playLinks:
label: Lien(s) pour jouer
help: Laissez vide pour afficher "à venir / coming soon"
type: url
# Sidebar - width: 3/10
sidebar:
width: 1/3
sections: sections:
meta: previewCol:
type: fields type: fields
fields: fields:
thumbnail: backgroundColor:
label: Vignette label: Couleur d'arrière plan
type: code-editor
language: css
size: custom-size
default: radial-gradient(circle at 20% 80%, rgb(240, 154, 110) 0%, rgb(233, 101, 122) 100%)
help: À remplir avec la valeur de la propriété CSS `background-color` souhaitée
preview:
label: Aperçu
type: files type: files
layout: cards layout: cards
size: small multiple: false
max: 1
accept: image/*
translate: false translate: false
image:
ratio: 1/1
uploads: uploads:
template: image template: image
help: Petite image carrée représentant le jeu help: Image affichée à droite de la description (sur ordinateur uniquement)
background_image:
label: Image d'arrière plan
type: files
layout: cards
max: 1
accept: image/*
translate: false
image:
ratio: 16/9
cover: true
uploads:
template: image
help: Image affichée en arrière-plan quand le jeu est sélectionné
play_links:
label: Lien(s) pour jouer
help: Sans lien renseigner, le jeu aura une étiquette "coming soon" et ne sera pas jouable.
type: structure
fields:
label:
label: Nom du bouton
required: true
type: text
width: 1/2
placeholder: "Ex: App Store, Site web..."
url:
label: URL
required: true
type: url
width: 1/2

View file

@ -15,7 +15,7 @@ tabs:
columns: columns:
- width: 2/3 - width: 2/3
sections: sections:
hero_content: heroContent:
type: fields type: fields
fields: fields:
subtitle: subtitle:
@ -40,7 +40,7 @@ tabs:
media: media:
type: fields type: fields
fields: fields:
background_video: backgroundVideo:
label: Vidéo d'arrière-plan label: Vidéo d'arrière-plan
type: files type: files
layout: cards layout: cards

View file

@ -12,7 +12,7 @@ tabs:
label: Jeux label: Jeux
icon: grid icon: grid
sections: sections:
games_list: gamesList:
type: pages type: pages
headline: Liste des jeux headline: Liste des jeux
layout: cards layout: cards
@ -21,5 +21,5 @@ tabs:
query: page.thumbnail.toFile query: page.thumbnail.toFile
cover: true cover: true
template: game template: game
info: "{{ page.status_label }}" info: "{{ page.statusLabel }}"
create: game create: game

View file

@ -14,7 +14,7 @@ tabs:
columns: columns:
- width: 1/3 - width: 1/3
sections: sections:
meta_fields: metaFields:
type: fields type: fields
fields: fields:
backgroundImage: backgroundImage:
@ -30,7 +30,7 @@ tabs:
ratio: 15/9 ratio: 15/9
- width: 2/3 - width: 2/3
sections: sections:
projects_list: projectsList:
type: pages type: pages
headline: Liste des projets headline: Liste des projets
layout: cards layout: cards

View file

@ -52,7 +52,7 @@ columns:
images: images:
type: fields type: fields
fields: fields:
images_gallery: imagesGallery:
width: 2/3 width: 2/3
label: Galerie d'image label: Galerie d'image
type: files type: files
@ -123,7 +123,7 @@ columns:
links: links:
type: fields type: fields
fields: fields:
external_links: externalLinks:
label: Liens externes label: Liens externes
type: structure type: structure
fields: fields:

View file

@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.php]
indent_size = 4

6
site/plugins/code-editor/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.DS_Store
.cache
node_modules
package-lock.json
yarn.lock
composer.lock

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Sylvain Julé
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,111 @@
# Kirby Code editor
Code editor field for Kirby 3, 4 and 5.
![screenshot-code-editor](https://user-images.githubusercontent.com/14079751/109679014-7b043800-7b7b-11eb-8c4e-2ae25da8288d.png)
<br/>
## Overview
> This plugin is completely free and published under the MIT license. However, if you are using it in a commercial project and want to help me keep up with maintenance, you can consider [making a donation of your choice](https://paypal.me/sylvainjl).
- [1. Installation](#1-installation)
- [2. Setup](#2-setup)
- [3. Options](#3-options)
- [4. Available languages](#4-available-languages)
- [5. License](#5-license)
- [6. Credits](#6-credits)
<br/>
## 1. Installation
Download and copy this repository to ```/site/plugins/code-editor```
Alternatively, you can install it with composer: ```composer require sylvainjule/code-editor```
<br/>
## 2. Setup
This field adds a code editor in the panel:
```yaml
editor:
label: My code editor
type: code-editor
```
<br/>
## 3. Options
| Name | Type | Default | Options | Description |
| -------------------- | ------------------ | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| language | `String` | `'css'` | - | Syntax mode of the editor. See below for available languages |
| size | `String` | `'small'` | - | Min height of the editor. `small / medium / large / huge` |
| lineNumbers | `Boolean` | `true` | - | Whether to show line numbers. |
| tabSize | `number` | `4` | - | The number of characters to insert when pressing tab key. |
| insertSpaces | `boolean` | `true` | - | Whether to use spaces for indentation. If you set it to `false`, you might also want to set `tabSize` to `1` |
| ignoreTabKey | `boolean` | `false` | - | Whether the editor should ignore tab key presses so that keyboard users can tab past the editor. Users can toggle this behaviour using `Ctrl+Shift+M` (Mac) / `Ctrl+M` manually when this is `false`. |
Note that you can make the default height any height you want with some [custom panel CSS](https://getkirby.com/docs/reference/system/options/panel#custom-panel-css). First, set the `size` option to any string you'd like:
```yaml
size: custom-size
```
Then in your `panel.css`:
```css
.k-code-editor-input[data-size="custom-size"] {
min-height: 15rem;
}
```
### 3.1. Default options
You can globally override the default options, instead of setting them on a per-field basis. In your `site/config/config.php`:
```php
return [
'sylvainjule.code-editor.language' => 'css',
'sylvainjule.code-editor.size' => 'small',
'sylvainjule.code-editor.lineNumbers' => true,
'sylvainjule.code-editor.tabSize' => 4,
'sylvainjule.code-editor.insertSpaces' => true,
'sylvainjule.code-editor.ignoreTabKey' => false,
];
```
<br/>
## 4. Available languages
Currently supported languages are:
* `css`
* `javascript`
* `json`
* `less`
* `php`
* `python`
* `ruby`
* `scss`
* `yaml`
<br/>
## 5. License
MIT
<br/>
## 6. Credits
**Code editor:**
- [Vue Prism Editor](https://github.com/koca/vue-prism-editor)

View file

@ -0,0 +1,20 @@
{
"name": "sylvainjule/code-editor",
"description": "Code editor field for Kirby 3, 4 and 5",
"type": "kirby-plugin",
"license": "MIT",
"version": "1.1.0",
"authors": [
{
"name": "Sylvain Julé",
"email": "contact@sylvain-jule.fr"
}
],
"require": {
"getkirby/composer-installer": "^1.2"
},
"extra": {
"installer-name": "code-editor"
},
"minimum-stability": "beta"
}

View file

@ -0,0 +1,28 @@
import js from "@eslint/js";
import prettier from "eslint-config-prettier";
import vue from "eslint-plugin-vue";
export default [
js.configs.recommended,
...vue.configs["flat/vue2-recommended"],
prettier,
{
rules: {
"vue/attributes-order": "error",
"vue/component-definition-name-casing": "off",
"vue/html-closing-bracket-newline": [
"error",
{
singleline: "never",
multiline: "always"
}
],
"vue/multi-word-component-names": "off",
"vue/require-default-prop": "off",
"vue/require-prop-types": "error"
},
languageOptions: {
ecmaVersion: 2022
}
}
];

View file

@ -0,0 +1 @@
.prism-editor-wrapper{width:100%;height:100%;display:flex;align-items:flex-start;overflow:auto;-o-tab-size:1.5em;tab-size:1.5em;-moz-tab-size:1.5em}@media (-ms-high-contrast:active),(-ms-high-contrast:none){.prism-editor-wrapper .prism-editor__textarea{color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::-moz-selection{background-color:#accef7!important;color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::selection{background-color:#accef7!important;color:transparent!important}}.prism-editor-wrapper .prism-editor__container{position:relative;text-align:left;box-sizing:border-box;padding:0;overflow:hidden;width:100%}.prism-editor-wrapper .prism-editor__line-numbers{height:100%;overflow:hidden;flex-shrink:0;padding-top:4px;margin-top:0;margin-right:10px}.prism-editor-wrapper .prism-editor__line-number{text-align:right;white-space:nowrap}.prism-editor-wrapper .prism-editor__textarea{position:absolute;top:0;left:0;height:100%;width:100%;resize:none;color:inherit;overflow:hidden;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;-webkit-text-fill-color:transparent}.prism-editor-wrapper .prism-editor__editor,.prism-editor-wrapper .prism-editor__textarea{margin:0;border:0;background:none;box-sizing:inherit;display:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-variant-ligatures:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;-moz-tab-size:inherit;-o-tab-size:inherit;tab-size:inherit;text-indent:inherit;text-rendering:inherit;text-transform:inherit;white-space:pre-wrap;word-wrap:keep-all;overflow-wrap:break-word;padding:0}.prism-editor-wrapper .prism-editor__textarea--empty{-webkit-text-fill-color:inherit!important}.prism-editor-wrapper .prism-editor__editor{position:relative;pointer-events:none}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.k-code-editor-input{background:light-dark(var(--color-gray-950),var(--input-color-back));color:var(--color-gray-200);font-family:var(--font-mono);font-size:var(--text-sm);line-height:1.5;padding:var(--spacing-2);border-radius:var(--rounded)}.k-code-editor-input[data-size=small]{min-height:7.5rem}.k-code-editor-input[data-size=medium]{min-height:15rem}.k-code-editor-input[data-size=large]{min-height:30rem}.k-code-editor-input[data-size=huge]{min-height:45rem}.prism-editor__textarea:focus{outline:none}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,15 @@
<?php
Kirby::plugin('sylvainjule/code-editor', [
'options' => array(
'language' => 'css',
'size' => 'small',
'lineNumbers' => true,
'tabSize' => 4,
'insertSpaces' => true,
'ignoreTabKey' => false,
),
'fields' => array(
'code-editor' => require_once __DIR__ . '/lib/fields/code-editor.php',
),
]);

View file

@ -0,0 +1,32 @@
<?php
$options = require kirby()->root('kirby') . '/config/fields/textarea.php';
/* Merge new properties
--------------------------------*/
$options = A::merge($options, [
'props' => [
'size' => function($size = null) {
return $size ?? option('sylvainjule.code-editor.size');
},
'language' => function($language = null) {
return $language ?? option('sylvainjule.code-editor.language');
},
'lineNumbers' => function($lineNumbers = null) {
return $lineNumbers ?? option('sylvainjule.code-editor.lineNumbers');
},
'tabSize' => function($tabSize = null) {
return $tabSize ?? option('sylvainjule.code-editor.tabSize');
},
'insertSpaces' => function($insertSpaces = null) {
return $tabSize ?? option('sylvainjule.code-editor.insertSpaces');
},
'ignoreTabKey' => function($ignoreTabKey = null) {
return $tabSize ?? option('sylvainjule.code-editor.ignoreTabKey');
},
]
]);
// return the updated options
return $options;

View file

@ -0,0 +1,30 @@
{
"name": "kirby-code-editor",
"version": "1.0.3",
"description": "Code editor field for Kirby 3 and 4",
"main": "index.js",
"author": "Kirby Community",
"license": "MIT",
"repository": {
"type": "git",
"url": "git@github.com:sylvainjule/kirby-code-editor.git"
},
"scripts": {
"dev": "kirbyup src/index.js --watch",
"build": "kirbyup src/index.js",
"lint": "eslint \"src/**/*.{js,vue}\"",
"lint:fix": "npm run lint -- --fix",
"format": "prettier --write \"src/**/*.{css,js,vue}\"",
"prepare": "node src/node/patchVuePrismEditor.mjs"
},
"devDependencies": {
"consola": "^3.4.2",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-vue": "^9.33.0",
"kirbyup": "^3.3.0",
"prettier": "^3.5.3",
"prismjs": "^1.30.0",
"vue-prism-editor": "^1.3.0"
}
}

View file

@ -0,0 +1,97 @@
<template>
<k-field :input="uid" v-bind="$props" class="k-code-editor-field">
<prism-editor
v-model="code"
class="k-code-editor-input"
:highlight="highlighter"
:line-numbers="lineNumbers"
:tab-size="tabSize"
:insert-spaces="insertSpaces"
:ignore-tab-key="ignoreTabKey"
:data-size="size"
@input="onCodeInput"
/>
</k-field>
</template>
<script>
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-markup-templating";
import "prismjs/components/prism-clike";
import "prismjs/components/prism-css";
import "prismjs/components/prism-javascript";
import "prismjs/components/prism-json";
import "prismjs/components/prism-less";
import "prismjs/components/prism-php";
import "prismjs/components/prism-python";
import "prismjs/components/prism-ruby";
import "prismjs/components/prism-scss";
import "prismjs/components/prism-yaml";
import "prismjs/themes/prism-tomorrow.css";
export default {
components: { PrismEditor },
extends: "k-textarea-field",
props: {
size: String,
language: String,
lineNumbers: Boolean,
tabSize: Number,
insertSpaces: Boolean,
ignoreTabKey: Boolean,
},
data() {
return {
code: "",
};
},
mounted() {
this.code = this.value;
},
methods: {
highlighter() {
return highlight(this.code, languages[this.language]);
},
onCodeInput() {
this.$emit("input", this.code);
},
},
};
</script>
<style>
.k-code-editor-input {
background: light-dark(var(--color-gray-950), var(--input-color-back));
color: var(--color-gray-200);
font-family: var(--font-mono);
font-size: var(--text-sm);
line-height: 1.5;
padding: var(--spacing-2);
border-radius: var(--rounded);
}
.k-code-editor-input[data-size="small"] {
min-height: 7.5rem;
}
.k-code-editor-input[data-size="medium"] {
min-height: 15rem;
}
.k-code-editor-input[data-size="large"] {
min-height: 30rem;
}
.k-code-editor-input[data-size="huge"] {
min-height: 45rem;
}
.prism-editor__textarea:focus {
outline: none;
}
</style>

View file

@ -0,0 +1,7 @@
import CodeEditor from "./components/field/CodeEditor.vue";
window.panel.plugin("sylvainjule/code-editor", {
fields: {
"code-editor": CodeEditor,
},
});

View file

@ -0,0 +1,38 @@
import { existsSync, readFileSync, writeFileSync } from "fs";
import chalk from "chalk";
import consola from "consola";
const srcPath = "node_modules/vue-prism-editor/dist/prismeditor.esm.js";
async function main() {
consola.start("Vue Prism Editor patcher");
if (!existsSync(srcPath)) {
consola.error(
`couldn't find ${chalk.cyan(srcPath)}, did you run ${chalk.green(
"npm install"
)}?`
);
return;
}
const source = readFileSync(srcPath, "utf8");
if (!source.includes("Vue.extend")) {
consola.success("already patched");
return;
}
consola.info("patching the source component...");
let output = source
.replace(/^import Vue from 'vue';/, "")
.replace("/*#__PURE__*/Vue.extend(", "")
.replace(/\}\)(;\s+export)/, "}$1");
writeFileSync(srcPath, output, "utf8");
consola.success("successfully patched");
}
main().catch((err) => consola.error(err));

View file

@ -2,20 +2,20 @@
$specificData = [ $specificData = [
'intro' => [ 'intro' => [
'title' => $page->intro_title()->value(), 'title' => $page->introTitle()->value(),
'text' => $page->intro_text()->value() 'text' => $page->introText()->value()
], ],
'mission' => [ 'mission' => [
'title' => $page->mission_title()->value(), 'title' => $page->missionTitle()->value(),
'text' => $page->mission_text()->toBlocks() 'text' => $page->missionText()->toBlocks()
], ],
'manifesto' => [ 'manifesto' => [
'title' => $page->manifesto_title()->value(), 'title' => $page->manifestoTitle()->value(),
'text' => $page->manifesto_text()->toBlocks() 'text' => $page->manifestoText()->toBlocks()
], ],
'team' => [ 'team' => [
'title' => $page->team_title()->value(), 'title' => $page->teamTitle()->value(),
'members' => $page->team_members()->toStructure()->map(function($member) { 'members' => $page->teamMembers()->toStructure()->map(function($member) {
return [ return [
'name' => $member->name()->value(), 'name' => $member->name()->value(),
'role' => $member->role()->value(), 'role' => $member->role()->value(),

View file

@ -1,21 +1,21 @@
<?php <?php
$related = $page->related_articles()->toPages(); $related = $page->relatedArticles()->toPages();
if ($related->isEmpty()) { if ($related->isEmpty()) {
$related = $page->siblings()->listed()->not($page)->shuffle()->limit(3); $related = $page->siblings()->listed()->not($page)->shuffle()->limit(3);
} }
$specificData = [ $specificData = [
'date' => $page->date()->toDate('Y-m-d'), 'date' => $page->date()->toDate('Y-m-d'),
'date_formatted' => $page->date()->toDate('d/m/Y'), 'dateFormatted' => $page->date()->toDate('d/m/Y'),
'intro' => $page->intro()->value(), 'intro' => $page->intro()->value(),
'author' => [ 'author' => [
'name' => $page->author_name()->value(), 'name' => $page->authorName()->value(),
'role' => $page->author_role()->value(), 'role' => $page->authorRole()->value(),
'photo' => $page->author_photo()->toFile()?->url() 'photo' => $page->authorPhoto()->toFile()?->url()
], ],
'cover' => $page->cover()->toFile()?->url(), 'cover' => $page->cover()->toFile()?->url(),
'content' => $page->article_content()->toBlocks(), 'content' => $page->articleContent()->toBlocks(),
'tags' => $page->tags()->split(), 'tags' => $page->tags()->split(),
'related' => $related->map(function($rec) { 'related' => $related->map(function($rec) {
return [ return [
@ -25,7 +25,7 @@ $specificData = [
'cover' => $rec->cover()->toFile()?->thumb(['width' => 400])->url() 'cover' => $rec->cover()->toFile()?->thumb(['width' => 400])->url()
]; ];
})->values(), })->values(),
'parent_url' => $page->parent()->url() 'parentUrl' => $page->parent()->url()
]; ];
$pageData = array_merge($genericData, $specificData); $pageData = array_merge($genericData, $specificData);

View file

@ -2,8 +2,8 @@
$specificData = [ $specificData = [
'intro' => [ 'intro' => [
'title' => $page->intro_title()->value(), 'title' => $page->introTitle()->value(),
'text' => $page->intro_text()->value() 'text' => $page->introText()->value()
], ],
'articles' => $page->children()->listed()->sortBy('date', 'desc')->map(function($article) { 'articles' => $page->children()->listed()->sortBy('date', 'desc')->map(function($article) {
return [ return [
@ -11,11 +11,11 @@ $specificData = [
'slug' => $article->slug(), 'slug' => $article->slug(),
'url' => $article->url(), 'url' => $article->url(),
'date' => $article->date()->toDate('Y-m-d'), 'date' => $article->date()->toDate('Y-m-d'),
'date_formatted' => $article->date()->toDate('d/m/Y'), 'dateFormatted' => $article->date()->toDate('d/m/Y'),
'intro' => $article->intro()->excerpt(200), 'intro' => $article->intro()->excerpt(200),
'cover' => $article->cover()->toFile()?->url(), 'cover' => $article->cover()->toFile()?->url(),
'author_name' => $article->author_name()->value(), 'authorName' => $article->authorName()->value(),
'author_photo' => $article->author_photo()->toFile()?->url() 'authorPhoto' => $article->authorPhoto()->toFile()?->url()
]; ];
})->values() })->values()
]; ];

View file

@ -3,11 +3,11 @@
$specificData = [ $specificData = [
'description' => $page->description()->value(), 'description' => $page->description()->value(),
'rules' => $page->rules()->toBlocks(), 'rules' => $page->rules()->toBlocks(),
'game_status' => $page->game_status()->value(), 'gameStatus' => $page->gameStatus()->value(),
'is_embedded' => $page->is_embedded()->toBool(), 'isEmbedded' => $page->isEmbedded()->toBool(),
'play_link' => $page->play_link()->value(), 'playLink' => $page->playLink()->value(),
'cover' => $page->cover()->toFile()?->url(), 'cover' => $page->cover()->toFile()?->url(),
'parent_url' => $page->parent()->url() 'parentUrl' => $page->parent()->url()
]; ];
$pageData = array_merge($genericData, $specificData); $pageData = array_merge($genericData, $specificData);

View file

@ -2,15 +2,15 @@
$specificData = [ $specificData = [
'hero' => [ 'hero' => [
'title' => $page->hero_title()->value(), 'title' => $page->heroTitle()->value(),
'title_highlight' => $page->hero_title_highlight()->value(), 'titleHighlight' => $page->heroTitleHighlight()->value(),
'subtitle' => $page->hero_subtitle()->value(), 'subtitle' => $page->heroSubtitle()->value(),
'ctaText' => $page->ctaText()->value(), 'ctaText' => $page->ctaText()->value(),
'ctaPath' => $page->ctaLink()->toPage()?->id() ?? '#', 'ctaPath' => $page->ctaLink()->toPage()?->id() ?? '#',
'image' => $page->hero_image()->toFile()?->url() 'image' => $page->heroImage()->toFile()?->url()
], ],
'background_video' => $page->background_video()->toFile()?->url(), 'backgroundVideo' => $page->backgroundVideo()->toFile()?->url(),
'floating_bubbles' => $page->floating_bubbles()->toStructure()->map(function($bubble) { 'floatingBubbles' => $page->floatingBubbles()->toStructure()->map(function($bubble) {
return [ return [
'text' => $bubble->text()->value(), 'text' => $bubble->text()->value(),
'position' => $bubble->position()->value() 'position' => $bubble->position()->value()

View file

@ -8,11 +8,9 @@ $specificData = [
'lettering' => $game->lettering()->toFile()?->url(), 'lettering' => $game->lettering()->toFile()?->url(),
'description' => $game->description()->value(), 'description' => $game->description()->value(),
'thumbnail' => $game->thumbnail()->toFile()?->url(), 'thumbnail' => $game->thumbnail()->toFile()?->url(),
'background_image' => $game->background_image()->toFile()?->url(), 'backgroundImage' => $game->backgroundImage()->toFile()?->url(),
'play_links' => $game->play_links()->toStructure()->map(fn($l) => [ 'backgroundColor' => $game->backgroundColor()->value() ?: null,
'label' => $l->label()->value(), 'playLink' => $game->playLinks()->value() ?: null,
'url' => $l->url()->value(),
])->values(),
]; ];
})->values() })->values()
]; ];

View file

@ -1,26 +1,26 @@
<?php <?php
$specificData = [ $specificData = [
'background_image' => $page->backgroundImage()->toFile()?->url(), 'backgroundImage' => $page->backgroundImage()->toFile()?->url(),
'projects' => $page->children()->listed()->map(function($project) { 'projects' => $page->children()->listed()->map(function($project) {
return [ return [
'title' => $project->title()->value(), 'title' => $project->title()->value(),
'slug' => $project->slug(), 'slug' => $project->slug(),
'catchphrase' => $project->catchphrase()->value(), 'catchPhrase' => $project->catchPhrase()->value(),
'description' => $project->description()->value(), 'description' => $project->description()->value(),
'thumbnail' => $project->thumbnail()->toFile()?->url(), 'thumbnail' => $project->thumbnail()->toFile()?->url(),
'images_gallery' => $project->images_gallery()->toFiles()->map(fn($f) => [ 'imagesGallery' => $project->imagesGallery()->toFiles()->map(fn($f) => [
'src' => $f->url(), 'src' => $f->url(),
'srcset' => $f->srcset('gallery'), 'srcset' => $f->srcset('gallery'),
'webp' => $f->srcset('gallery-webp'), 'webp' => $f->srcset('gallery-webp'),
])->values(), ])->values(),
'mockup' => $project->mockup()->toFile()?->url(), 'mockup' => $project->mockup()->toFile()?->url(),
'gallery_background_color' => $project->galleryBackgroundColor()->value(), 'galleryBackgroundColor' => $project->galleryBackgroundColor()->value(),
'keywords' => $project->keywords()->toStructure()->map(fn($i) => [ 'keywords' => $project->keywords()->toStructure()->map(fn($i) => [
'label' => $i->label()->value(), 'label' => $i->label()->value(),
'text' => $i->text()->value(), 'text' => $i->text()->value(),
])->values(), ])->values(),
'external_links' => $project->external_links()->toStructure()->map(fn($i) => [ 'externalLinks' => $project->externalLinks()->toStructure()->map(fn($i) => [
'label' => $i->label()->value(), 'label' => $i->label()->value(),
'url' => $i->url()->value(), 'url' => $i->url()->value(),
])->values(), ])->values(),

View file

@ -10,10 +10,10 @@ $specificData = [
'impact' => $page->impact()->split(','), 'impact' => $page->impact()->split(','),
'category' => $page->category()->value(), 'category' => $page->category()->value(),
'platforms' => $page->platforms()->split(','), 'platforms' => $page->platforms()->split(','),
'client_name' => $page->client_name()->value(), 'clientName' => $page->clientName()->value(),
'apple_link' => $page->apple_link()->value(), 'appleLink' => $page->appleLink()->value(),
'android_link' => $page->android_link()->value(), 'androidLink' => $page->androidLink()->value(),
'web_link' => $page->web_link()->value(), 'webLink' => $page->webLink()->value(),
'prev' => $page->prev() ? [ 'prev' => $page->prev() ? [
'title' => $page->prev()->title()->value(), 'title' => $page->prev()->title()->value(),
'url' => $page->prev()->url() 'url' => $page->prev()->url()
@ -22,7 +22,7 @@ $specificData = [
'title' => $page->next()->title()->value(), 'title' => $page->next()->title()->value(),
'url' => $page->next()->url() 'url' => $page->next()->url()
] : null, ] : null,
'parent_url' => $page->parent()->url() 'parentUrl' => $page->parent()->url()
]; ];
$pageData = array_merge($genericData, $specificData); $pageData = array_merge($genericData, $specificData);

View file

@ -71,9 +71,6 @@
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
padding: 2vh 0; padding: 2vh 0;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(15px);
transition: 0.3s;
} }
/* Logo */ /* Logo */

View file

@ -48,6 +48,16 @@
hue-rotate(91deg) brightness(101%) contrast(97%); hue-rotate(91deg) brightness(101%) contrast(97%);
} }
.button.play-icon::before {
content: "";
background-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7598 13.5H14.6348V14.625V15.75H15.7598V14.625H16.8848V13.5V12.375H15.7598V13.5Z' fill='black'/%3E%3Cpath d='M12.3848 15.75V16.875H13.5098H14.6348V15.75H13.5098H12.3848Z' fill='black'/%3E%3Cpath d='M10.1348 16.875H9.00977V18H10.1348H11.2598H12.3848V16.875H11.2598H10.1348Z' fill='black'/%3E%3Cpath d='M1.125 10.125V9V7.875V6.75V5.625H0V6.75V7.875V9V10.125V11.25V12.375H1.125V11.25V10.125Z' fill='black'/%3E%3Cpath d='M3.375 13.5H2.25V12.375H1.125V13.5V14.625H2.25V15.75H3.375V14.625V13.5Z' fill='black'/%3E%3Cpath d='M4.5 15.75H3.375V16.875H4.5H5.625V15.75H4.5Z' fill='black'/%3E%3Cpath d='M6.75 16.875H5.625V18H6.75H7.875H9V16.875H7.875H6.75Z' fill='black'/%3E%3Cpath d='M16.8848 5.625V6.75V7.875V9V10.125V11.25V12.375H18.0098V11.25V10.125V9V7.875V6.75V5.625H16.8848Z' fill='black'/%3E%3Cpath d='M16.8848 3.375H15.7598V2.25H14.6348V3.375V4.5H15.7598V5.625H16.8848V4.5V3.375Z' fill='black'/%3E%3Cpath d='M11.2598 0H10.1348H9.00977V1.125H10.1348H11.2598H12.3848V0H11.2598Z' fill='black'/%3E%3Cpath d='M13.5098 1.125H12.3848V2.25H13.5098H14.6348V1.125H13.5098Z' fill='black'/%3E%3Cpath d='M2.25 4.5H3.375V3.375V2.25H2.25V3.375H1.125V4.5V5.625H2.25V4.5Z' fill='black'/%3E%3Cpath d='M5.625 2.25V1.125H4.5H3.375V2.25H4.5H5.625Z' fill='black'/%3E%3Cpath d='M7.875 1.125H9V0H7.875H6.75H5.625V1.125H6.75H7.875Z' fill='black'/%3E%3Cpath d='M10.1399 10.1825H11.2774V9.6775H12.4149V8.54H11.2774V7.91H10.1399V6.7725H9.00492V5.6375H7.86742V4.5H6.73242V13.5925H7.86742V12.455H9.00492V11.3175H10.1399V10.1825Z' fill='black'/%3E%3C/svg%3E");
background-size: 100%;
width: 1.125rem;
height: 1.125rem;
transform: translateX(-0.5rem);
transition: all 0.5s ease-out;
}
/* Clickable elements */ /* Clickable elements */
.clickable { .clickable {
cursor: pointer; cursor: pointer;

View file

@ -61,22 +61,71 @@
}) })
</script> </script>
<section class="play golden-grid slide" aria-label="Jouer"> <section
class="play golden-grid slide"
aria-label="Jouer"
style={displayedGame?.backgroundColor ? `--background-color: ${displayedGame.backgroundColor}` : ''}
>
<!-- Fond : image + overlay, crossfade au changement de jeu --> <!-- Fond : image + overlay, crossfade au changement de jeu -->
<div class="play-bg" class:is-out={isOut} aria-hidden="true"> <div class="play-bg" class:is-out={isOut} aria-hidden="true">
{#if displayedGame?.background_image} {#if displayedGame?.backgroundImage}
<img class="play-bg-img" src={displayedGame.background_image} alt="" /> <img class="play-bg-img" src={displayedGame.backgroundImage} alt="" />
{/if} {/if}
<div class="play-bg-overlay"></div> <div class="play-bg-overlay"></div>
</div> </div>
<!-- Lignes verticales décoratives --> <!-- Lignes courbes décoratives (SVG) -->
<div class="vertical-line-start" aria-hidden="true"></div> <div class="play-curves" aria-hidden="true">
<div class="vertical-line vertical-line-col8" aria-hidden="true"></div> <svg viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<div class="vertical-line-center" aria-hidden="true"></div> <g opacity="0.2">
<div class="vertical-line vertical-line-col14" aria-hidden="true"></div> <mask id="play-curves-mask" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="5" y="-16" width="1915" height="1086">
<div class="vertical-line-end" aria-hidden="true"></div> <path d="M1919.57 -15.1654H5.50122V1069.95H1919.57V-15.1654Z" fill="white"/>
</mask>
<g mask="url(#play-curves-mask)">
<path d="M-183.792 913.715L2331.66 833.813" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 836.732L2331.66 692.277" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 759.749L2331.66 550.74" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 682.765L2331.66 409.204" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 605.782L2331.66 267.633" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 528.799L2331.66 126.096" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 451.816L2331.66 -15.4402" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 374.867L2331.66 -156.977" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 297.884L2331.66 -298.513" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 220.901L2331.66 -440.05" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 143.917L2331.66 -581.621" stroke="white" stroke-miterlimit="10"/>
<path d="M-183.792 66.9339L2331.66 -723.157" stroke="white" stroke-miterlimit="10"/>
<path d="M-1449.15 1070.43L-398.072 931.399C-15.9165 865.61 39.8245 286.038 39.8245 286.038L96.3206 -98.8099" stroke="white" stroke-width="0.78" stroke-miterlimit="10"/>
<path d="M-1319.96 1070.91L-246.878 926.111C143.275 857.609 200.183 253.967 200.183 253.967L257.846 -146.847" stroke="white" stroke-width="0.8" stroke-miterlimit="10"/>
<path d="M-1190.77 1071.39L-95.6497 920.823C302.501 849.574 360.576 221.896 360.576 221.896L419.406 -194.885" stroke="white" stroke-width="0.82" stroke-miterlimit="10"/>
<path d="M-1061.54 1071.87L55.5788 915.535C461.726 841.574 520.968 189.826 520.968 189.826L581 -242.922" stroke="white" stroke-width="0.85" stroke-miterlimit="10"/>
<path d="M-932.349 1072.35L206.773 910.247C620.918 833.539 681.361 157.755 681.361 157.755L742.559 -290.959" stroke="white" stroke-width="0.87" stroke-miterlimit="10"/>
<path d="M-803.156 1072.8L357.967 904.925C780.144 825.504 841.72 125.65 841.72 125.65L904.119 -339.031" stroke="white" stroke-width="0.9" stroke-miterlimit="10"/>
<path d="M-673.963 1073.28L509.161 899.637C939.335 817.469 1002.08 93.5794 1002.08 93.5794L1065.64 -387.068" stroke="white" stroke-width="0.92" stroke-miterlimit="10"/>
<path d="M-544.736 1073.76L660.424 894.35C1098.6 809.469 1162.51 61.5431 1162.51 61.5431L1227.27 -435.071" stroke="white" stroke-width="0.94" stroke-miterlimit="10"/>
<path d="M-415.543 1074.24L811.618 889.062C1257.79 801.434 1322.86 29.4725 1322.86 29.4725L1388.8 -483.108" stroke="white" stroke-width="0.97" stroke-miterlimit="10"/>
<path d="M-286.35 1074.72L962.812 883.774C1416.98 793.433 1483.22 -2.59817 1483.22 -2.59817L1550.36 -531.146" stroke="white" stroke-width="0.99" stroke-miterlimit="10"/>
<path d="M-157.157 1075.2L1114.01 878.486C1576.17 785.398 1643.58 -34.6688 1643.58 -34.6688L1711.88 -579.183" stroke="white" stroke-width="1.01" stroke-miterlimit="10"/>
<path d="M-27.9639 1075.65L1265.2 873.163C1735.36 777.364 1803.97 -66.7738 1803.97 -66.7738L1873.48 -627.254" stroke="white" stroke-width="1.04" stroke-miterlimit="10"/>
<path d="M101.263 1076.13L1416.46 867.876C1894.62 769.329 1964.4 -98.8443 1964.4 -98.8443L2035.07 -675.292" stroke="white" stroke-width="1.06" stroke-miterlimit="10"/>
<path d="M230.456 1076.61L1567.66 862.588C2053.85 761.328 2124.76 -130.915 2124.76 -130.915L2196.6 -723.329" stroke="white" stroke-width="1.09" stroke-miterlimit="10"/>
<path d="M359.649 1077.09L1718.85 857.3C2213.04 753.293 2285.12 -162.986 2285.12 -162.986L2358.16 -771.366" stroke="white" stroke-width="1.11" stroke-miterlimit="10"/>
<path d="M488.842 1077.57L1870.05 852.012C2372.23 745.293 2445.48 -195.056 2445.48 -195.056L2519.68 -819.403" stroke="white" stroke-width="1.13" stroke-miterlimit="10"/>
<path d="M618.035 1078.05L2021.24 846.724C2531.42 737.258 2605.83 -227.127 2605.83 -227.127L2681.24 -867.441" stroke="white" stroke-width="1.16" stroke-miterlimit="10"/>
<path d="M747.262 1078.5L2172.47 841.402C2690.65 729.223 2766.23 -259.197 2766.23 -259.197L2842.8 -915.478" stroke="white" stroke-width="1.18" stroke-miterlimit="10"/>
<path d="M876.455 1078.98L2323.7 836.114C2849.87 721.189 2926.62 -291.268 2926.62 -291.268L3004.4 -963.515" stroke="white" stroke-width="1.2" stroke-miterlimit="10"/>
<path d="M1005.65 1079.46L2474.89 830.827C3009.06 713.188 3087.01 -323.339 3087.01 -323.339L3165.96 -1011.55" stroke="white" stroke-width="1.23" stroke-miterlimit="10"/>
<path d="M1134.84 1079.94L2626.08 825.538C3168.26 705.153 3247.37 -355.409 3247.37 -355.409L3327.52 -1059.62" stroke="white" stroke-width="1.25" stroke-miterlimit="10"/>
<path d="M1264.07 1080.42L2777.31 820.25C3327.48 697.153 3407.76 -387.48 3407.76 -387.48L3489.08 -1107.66" stroke="white" stroke-width="1.28" stroke-miterlimit="10"/>
<path d="M1393.26 1080.9L2928.51 814.963C3486.71 689.118 3568.12 -419.551 3568.12 -419.551L3650.64 -1155.7" stroke="white" stroke-width="1.3" stroke-miterlimit="10"/>
<path d="M1522.45 1081.35L3079.74 809.64C3645.93 681.083 3728.52 -451.656 3728.52 -451.656L3812.2 -1203.77" stroke="white" stroke-width="1.32" stroke-miterlimit="10"/>
<path d="M1651.65 1081.83L3230.93 804.353C3805.12 673.048 3888.87 -483.726 3888.87 -483.726L3973.72 -1251.81" stroke="white" stroke-width="1.35" stroke-miterlimit="10"/>
<path d="M1780.84 1082.31L3382.12 799.065C3964.32 665.048 4049.23 -515.797 4049.23 -515.797L4135.31 -1299.84" stroke="white" stroke-width="1.37" stroke-miterlimit="10"/>
</g>
</g>
</svg>
</div>
<!-- Section principale : lettering + description + boutons de jeu --> <!-- Section principale : lettering + description + boutons de jeu -->
<div <div
@ -99,15 +148,13 @@
{/if} {/if}
<div class="play-actions"> <div class="play-actions">
{#if displayedGame.play_links?.length} {#if displayedGame.playLink}
{#each displayedGame.play_links as link} <a
<a href={displayedGame.playLink}
href={link.url} target="_blank"
target="_blank" rel="noopener noreferrer"
rel="noopener noreferrer" class="button play-icon"
class="button" >Jouer</a>
>{link.label}</a>
{/each}
{:else} {:else}
<button class="button" disabled>Coming soon</button> <button class="button" disabled>Coming soon</button>
{/if} {/if}
@ -139,7 +186,7 @@
<style> <style>
.play { .play {
background: #000; background: var(--background-color);
} }
/* --- Background --- */ /* --- Background --- */
@ -173,13 +220,17 @@
); );
} }
/* --- Lignes verticales --- */ /* --- Lignes courbes SVG --- */
.vertical-line-col8 { .play-curves {
grid-area: 1/8 / span 20 / span 1; grid-area: 1/1 / span 20 / span 20;
pointer-events: none;
z-index: 1;
overflow: hidden;
} }
.vertical-line-col14 { .play-curves svg {
grid-area: 1/14 / span 20 / span 1; width: 100%;
height: 100%;
} }
/* --- Section principale --- */ /* --- Section principale --- */
@ -199,8 +250,7 @@
} }
.play-lettering { .play-lettering {
max-height: 120px; width: clamp(180px,18.77vw,362px);
width: auto;
object-fit: contain; object-fit: contain;
object-position: left center; object-position: left center;
} }
@ -236,6 +286,9 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: clamp(140px,15.09vw,291px);
border-radius: 12px;
border: 2px solid transparent;
gap: 0.5rem; gap: 0.5rem;
background: none; background: none;
border: none; border: none;
@ -243,13 +296,13 @@
opacity: 0.5; opacity: 0.5;
transition: opacity 0.4s var(--ease-standard), transform 0.4s var(--ease-standard); transition: opacity 0.4s var(--ease-standard), transform 0.4s var(--ease-standard);
} }
.play-carousel-item.active button {
opacity: 1;
width: clamp(170px,18.41vw,355px);
}
.play-carousel-item button img { .play-carousel-item button img {
width: 64px;
height: 64px;
object-fit: cover; object-fit: cover;
border-radius: 12px;
border: 2px solid transparent;
transition: border-color 0.4s var(--ease-standard); transition: border-color 0.4s var(--ease-standard);
} }
@ -259,11 +312,6 @@
text-align: center; text-align: center;
} }
.play-carousel-item.active button {
opacity: 1;
transform: scale(1.15) translateY(-4px);
}
.play-carousel-item.active button img { .play-carousel-item.active button img {
border-color: var(--color-primary); border-color: var(--color-primary);
} }

View file

@ -13,7 +13,7 @@
// --- Derived --- // --- Derived ---
const isActive = $derived(slides.active?.id === 'portfolio') const isActive = $derived(slides.active?.id === 'portfolio')
const projects = $derived(data?.projects ?? []) const projects = $derived(data?.projects ?? [])
const backgroundImage = $derived(data?.background_image ?? null) const backgroundImage = $derived(data?.backgroundImage ?? null)
const currentProject = $derived(projects[currentIndex] ?? null) const currentProject = $derived(projects[currentIndex] ?? null)
// Capture du hash synchrone avant que tout effect puisse le modifier // Capture du hash synchrone avant que tout effect puisse le modifier
@ -96,7 +96,7 @@
{#if currentProject} {#if currentProject}
<!-- Galerie animation (gauche) --> <!-- Galerie animation (gauche) -->
<div class="portfolio-gallery" aria-hidden="true"> <div class="portfolio-gallery" aria-hidden="true">
<GalleryAnimation images={currentProject.images_gallery} backgroundColor={currentProject.gallery_background_color} /> <GalleryAnimation images={currentProject.imagesGallery} backgroundColor={currentProject.galleryBackgroundColor} />
</div> </div>
<!-- Mockup device (centre) --> <!-- Mockup device (centre) -->
@ -107,7 +107,7 @@
<!-- Infos projet (droite) --> <!-- Infos projet (droite) -->
<div class="portfolio-text" aria-live="polite"> <div class="portfolio-text" aria-live="polite">
<h2>{currentProject.title}</h2> <h2>{currentProject.title}</h2>
<h3 class="portfolio-catchphrase gradient-blue">{@html currentProject.catchphrase}</h3> <h3 class="portfolio-catchphrase gradient-blue">{@html currentProject.catchPhrase}</h3>
<div class="portfolio-description">{@html currentProject.description}</div> <div class="portfolio-description">{@html currentProject.description}</div>
<div class="portfolio-keywords"> <div class="portfolio-keywords">
{#each currentProject.keywords as kw} {#each currentProject.keywords as kw}
@ -115,7 +115,7 @@
{/each} {/each}
</div> </div>
<div class="portfolio-links"> <div class="portfolio-links">
{#each currentProject.external_links as link} {#each currentProject.externalLinks as link}
<a href={link.url} target="_blank" rel="noopener noreferrer" class="button earth-icon">{link.label}</a> <a href={link.url} target="_blank" rel="noopener noreferrer" class="button earth-icon">{link.label}</a>
{/each} {/each}
</div> </div>

View file

@ -16,7 +16,7 @@ return array(
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
'League\\ColorExtractor\\' => array($vendorDir . '/league/color-extractor/src'), 'League\\ColorExtractor\\' => array($vendorDir . '/league/color-extractor/src'),
'Laminas\\Escaper\\' => array($vendorDir . '/laminas/laminas-escaper/src'), 'Laminas\\Escaper\\' => array($vendorDir . '/laminas/laminas-escaper/src'),
'Kirby\\' => array($vendorDir . '/getkirby/composer-installer/src', $baseDir . '/kirby/src'), 'Kirby\\' => array($baseDir . '/kirby/src', $vendorDir . '/getkirby/composer-installer/src'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'), 'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'), 'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'),
); );

View file

@ -96,8 +96,8 @@ class ComposerStaticInitda1c3a8eb3e9e3b252ad405f32a3f585
), ),
'Kirby\\' => 'Kirby\\' =>
array ( array (
0 => __DIR__ . '/..' . '/getkirby/composer-installer/src', 0 => __DIR__ . '/../..' . '/kirby/src',
1 => __DIR__ . '/../..' . '/kirby/src', 1 => __DIR__ . '/..' . '/getkirby/composer-installer/src',
), ),
'Composer\\Semver\\' => 'Composer\\Semver\\' =>
array ( array (

View file

@ -748,6 +748,47 @@
}, },
"install-path": "../psr/log" "install-path": "../psr/log"
}, },
{
"name": "sylvainjule/code-editor",
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sylvainjule/kirby-code-editor.git",
"reference": "adbc2c8a728994cc57ea72a7f8628f27d202b8df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sylvainjule/kirby-code-editor/zipball/adbc2c8a728994cc57ea72a7f8628f27d202b8df",
"reference": "adbc2c8a728994cc57ea72a7f8628f27d202b8df",
"shasum": ""
},
"require": {
"getkirby/composer-installer": "^1.2"
},
"time": "2025-08-04T17:32:08+00:00",
"type": "kirby-plugin",
"extra": {
"installer-name": "code-editor"
},
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sylvain Julé",
"email": "contact@sylvain-jule.fr"
}
],
"description": "Code editor field for Kirby 3, 4 and 5",
"support": {
"issues": "https://github.com/sylvainjule/kirby-code-editor/issues",
"source": "https://github.com/sylvainjule/kirby-code-editor/tree/1.1.0"
},
"install-path": "../../site/plugins/code-editor"
},
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
"version": "v3.6.0", "version": "v3.6.0",

View file

@ -1,9 +1,9 @@
<?php return array( <?php return array(
'root' => array( 'root' => array(
'name' => 'getkirby/plainkit', 'name' => 'getkirby/plainkit',
'pretty_version' => '5.2.2', 'pretty_version' => 'dev-main',
'version' => '5.2.2.0', 'version' => 'dev-main',
'reference' => null, 'reference' => '3cafcf3167251bbbfd64183de5da67c4a488fa7d',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -65,9 +65,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'getkirby/plainkit' => array( 'getkirby/plainkit' => array(
'pretty_version' => '5.2.2', 'pretty_version' => 'dev-main',
'version' => '5.2.2.0', 'version' => 'dev-main',
'reference' => null, 'reference' => '3cafcf3167251bbbfd64183de5da67c4a488fa7d',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -124,6 +124,15 @@
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'sylvainjule/code-editor' => array(
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'reference' => 'adbc2c8a728994cc57ea72a7f8628f27d202b8df',
'type' => 'kirby-plugin',
'install_path' => __DIR__ . '/../../site/plugins/code-editor',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array( 'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.6.0', 'pretty_version' => 'v3.6.0',
'version' => '3.6.0.0', 'version' => '3.6.0.0',