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

View file

@ -22,7 +22,8 @@
},
"require": {
"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": {
"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",
"This file is @generated automatically"
],
"content-hash": "da1c3a8eb3e9e3b252ad405f32a3f585",
"content-hash": "07c4d6a85de1d10e6c6fc0e5cec09033",
"packages": [
{
"name": "christian-riesen/base32",
@ -721,6 +721,44 @@
},
"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",
"version": "v3.6.0",

View file

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

View file

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

View file

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

View file

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

View file

@ -1,98 +1,83 @@
title: Jeu
icon: play
# status:
# draft:
# label: Brouillon
# text: Le jeu n'est pas visible
# unlisted:
# label: Non listé
# text: Le jeu est accessible mais pas dans la liste
# listed:
# label: Publié
# text: Le jeu est visible publiquement
status:
draft:
label: Brouillon
text: Le jeu n'est pas visible
unlisted:
label: Non listé
text: Le jeu est accessible mais pas dans la liste
listed:
label: Public
text: Le jeu est visible publiquement
columns:
# Colonne principale
main:
width: 2/3
sections:
# Informations principales
content:
type: fields
fields:
lettering:
label: Lettrage du jeu
type: files
layout: cardlets
size: small
max: 1
accept: image/*
translate: false
image:
ratio: 1/1
uploads:
template: image
help: Image avec le lettrage du jeu
description:
label: Description
type: writer
marks:
- bold
- italic
- green
- pixel
- underline
- strike
- clear
- link
- width: 2/10
fields:
thumbnail:
label: Vignette
type: files
layout: cards
size: small
max: 1
accept: image/*
translate: false
image:
ratio: 1/1
uploads:
template: image
help: Pour le carousel de navigation parmi les jeux
- width: 2/10
fields:
lettering:
label: Lettrage
type: files
multiple: false
translate: false
layout: cards
size: tiny
uploads:
template: image
help: Affiché au-dessus de la description
width: 1/3
- width: 3/10
fields:
description:
label: Description
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
sidebar:
width: 1/3
- width: 3/10
sections:
meta:
previewCol:
type: fields
fields:
thumbnail:
label: Vignette
backgroundColor:
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
layout: cards
size: small
max: 1
accept: image/*
multiple: false
translate: false
image:
ratio: 1/1
uploads:
template: image
help: Petite image carrée représentant le jeu
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
help: Image affichée à droite de la description (sur ordinateur uniquement)

View file

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

View file

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

View file

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

View file

@ -52,7 +52,7 @@ columns:
images:
type: fields
fields:
images_gallery:
imagesGallery:
width: 2/3
label: Galerie d'image
type: files
@ -123,7 +123,7 @@ columns:
links:
type: fields
fields:
external_links:
externalLinks:
label: Liens externes
type: structure
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 = [
'intro' => [
'title' => $page->intro_title()->value(),
'text' => $page->intro_text()->value()
'title' => $page->introTitle()->value(),
'text' => $page->introText()->value()
],
'mission' => [
'title' => $page->mission_title()->value(),
'text' => $page->mission_text()->toBlocks()
'title' => $page->missionTitle()->value(),
'text' => $page->missionText()->toBlocks()
],
'manifesto' => [
'title' => $page->manifesto_title()->value(),
'text' => $page->manifesto_text()->toBlocks()
'title' => $page->manifestoTitle()->value(),
'text' => $page->manifestoText()->toBlocks()
],
'team' => [
'title' => $page->team_title()->value(),
'members' => $page->team_members()->toStructure()->map(function($member) {
'title' => $page->teamTitle()->value(),
'members' => $page->teamMembers()->toStructure()->map(function($member) {
return [
'name' => $member->name()->value(),
'role' => $member->role()->value(),

View file

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

View file

@ -2,8 +2,8 @@
$specificData = [
'intro' => [
'title' => $page->intro_title()->value(),
'text' => $page->intro_text()->value()
'title' => $page->introTitle()->value(),
'text' => $page->introText()->value()
],
'articles' => $page->children()->listed()->sortBy('date', 'desc')->map(function($article) {
return [
@ -11,11 +11,11 @@ $specificData = [
'slug' => $article->slug(),
'url' => $article->url(),
'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),
'cover' => $article->cover()->toFile()?->url(),
'author_name' => $article->author_name()->value(),
'author_photo' => $article->author_photo()->toFile()?->url()
'authorName' => $article->authorName()->value(),
'authorPhoto' => $article->authorPhoto()->toFile()?->url()
];
})->values()
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -48,6 +48,16 @@
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 {
cursor: pointer;

View file

@ -61,22 +61,71 @@
})
</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 -->
<div class="play-bg" class:is-out={isOut} aria-hidden="true">
{#if displayedGame?.background_image}
<img class="play-bg-img" src={displayedGame.background_image} alt="" />
{#if displayedGame?.backgroundImage}
<img class="play-bg-img" src={displayedGame.backgroundImage} alt="" />
{/if}
<div class="play-bg-overlay"></div>
</div>
<!-- Lignes verticales décoratives -->
<div class="vertical-line-start" aria-hidden="true"></div>
<div class="vertical-line vertical-line-col8" aria-hidden="true"></div>
<div class="vertical-line-center" aria-hidden="true"></div>
<div class="vertical-line vertical-line-col14" aria-hidden="true"></div>
<div class="vertical-line-end" aria-hidden="true"></div>
<!-- Lignes courbes décoratives (SVG) -->
<div class="play-curves" aria-hidden="true">
<svg viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.2">
<mask id="play-curves-mask" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="5" y="-16" width="1915" height="1086">
<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 -->
<div
@ -99,15 +148,13 @@
{/if}
<div class="play-actions">
{#if displayedGame.play_links?.length}
{#each displayedGame.play_links as link}
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
class="button"
>{link.label}</a>
{/each}
{#if displayedGame.playLink}
<a
href={displayedGame.playLink}
target="_blank"
rel="noopener noreferrer"
class="button play-icon"
>Jouer</a>
{:else}
<button class="button" disabled>Coming soon</button>
{/if}
@ -139,7 +186,7 @@
<style>
.play {
background: #000;
background: var(--background-color);
}
/* --- Background --- */
@ -173,13 +220,17 @@
);
}
/* --- Lignes verticales --- */
.vertical-line-col8 {
grid-area: 1/8 / span 20 / span 1;
/* --- Lignes courbes SVG --- */
.play-curves {
grid-area: 1/1 / span 20 / span 20;
pointer-events: none;
z-index: 1;
overflow: hidden;
}
.vertical-line-col14 {
grid-area: 1/14 / span 20 / span 1;
.play-curves svg {
width: 100%;
height: 100%;
}
/* --- Section principale --- */
@ -199,8 +250,7 @@
}
.play-lettering {
max-height: 120px;
width: auto;
width: clamp(180px,18.77vw,362px);
object-fit: contain;
object-position: left center;
}
@ -236,6 +286,9 @@
display: flex;
flex-direction: column;
align-items: center;
width: clamp(140px,15.09vw,291px);
border-radius: 12px;
border: 2px solid transparent;
gap: 0.5rem;
background: none;
border: none;
@ -243,13 +296,13 @@
opacity: 0.5;
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 {
width: 64px;
height: 64px;
object-fit: cover;
border-radius: 12px;
border: 2px solid transparent;
transition: border-color 0.4s var(--ease-standard);
}
@ -259,11 +312,6 @@
text-align: center;
}
.play-carousel-item.active button {
opacity: 1;
transform: scale(1.15) translateY(-4px);
}
.play-carousel-item.active button img {
border-color: var(--color-primary);
}

View file

@ -13,7 +13,7 @@
// --- Derived ---
const isActive = $derived(slides.active?.id === 'portfolio')
const projects = $derived(data?.projects ?? [])
const backgroundImage = $derived(data?.background_image ?? null)
const backgroundImage = $derived(data?.backgroundImage ?? null)
const currentProject = $derived(projects[currentIndex] ?? null)
// Capture du hash synchrone avant que tout effect puisse le modifier
@ -96,7 +96,7 @@
{#if currentProject}
<!-- Galerie animation (gauche) -->
<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>
<!-- Mockup device (centre) -->
@ -107,7 +107,7 @@
<!-- Infos projet (droite) -->
<div class="portfolio-text" aria-live="polite">
<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-keywords">
{#each currentProject.keywords as kw}
@ -115,7 +115,7 @@
{/each}
</div>
<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>
{/each}
</div>

View file

@ -16,7 +16,7 @@ return array(
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
'League\\ColorExtractor\\' => array($vendorDir . '/league/color-extractor/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'),
'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'),
);

View file

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

View file

@ -748,6 +748,47 @@
},
"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",
"version": "v3.6.0",

View file

@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'getkirby/plainkit',
'pretty_version' => '5.2.2',
'version' => '5.2.2.0',
'reference' => null,
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '3cafcf3167251bbbfd64183de5da67c4a488fa7d',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -65,9 +65,9 @@
'dev_requirement' => false,
),
'getkirby/plainkit' => array(
'pretty_version' => '5.2.2',
'version' => '5.2.2.0',
'reference' => null,
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '3cafcf3167251bbbfd64183de5da67c4a488fa7d',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -124,6 +124,15 @@
'aliases' => array(),
'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(
'pretty_version' => 'v3.6.0',
'version' => '3.6.0.0',