Added lilypond edition
This commit is contained in:
35
folkugat_web/assets/static/css/lilypond.css
Normal file
35
folkugat_web/assets/static/css/lilypond.css
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Override background and text color for the entire editor */
|
||||
.CodeMirror {
|
||||
background: #1e1e1e; /* Dark background */
|
||||
color: #f8f8f2; /* Light text */
|
||||
}
|
||||
|
||||
/* Optional: Style the gutter (line numbers) */
|
||||
.CodeMirror-gutters {
|
||||
background: #1e1e1e;
|
||||
border-right: 1px solid #444;
|
||||
}
|
||||
|
||||
/* Optional: Style active line */
|
||||
.CodeMirror-activeline-background {
|
||||
background: #2a2a2a !important;
|
||||
}
|
||||
|
||||
/* Make the cursor white */
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid white !important;
|
||||
}
|
||||
|
||||
/* Optional: Change the cursor color when focused/unfocused */
|
||||
.CodeMirror-focused .CodeMirror-cursor {
|
||||
border-left: 1px solid white !important;
|
||||
}
|
||||
|
||||
.cm-keyword {
|
||||
/* Commands like \relative */
|
||||
color: #569cd6;
|
||||
}
|
||||
.cm-comment {
|
||||
/* Notes like c4, d'8 */
|
||||
color: #5c5c5c;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
17
folkugat_web/assets/static/js/lilypond.js
Normal file
17
folkugat_web/assets/static/js/lilypond.js
Normal file
@@ -0,0 +1,17 @@
|
||||
CodeMirror.defineMode("lilypond", function() {
|
||||
return {
|
||||
token: function(stream) {
|
||||
// Highlight Comments
|
||||
if (stream.match(/%.*$/)) {
|
||||
return "comment";
|
||||
}
|
||||
// Highlight LilyPond commands (e.g., \relative, \time, \key)
|
||||
if (stream.match(/\\[a-zA-Z]+/)) {
|
||||
return "keyword";
|
||||
}
|
||||
// Move to the next character
|
||||
stream.next();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -7,7 +7,8 @@
|
||||
</h3>
|
||||
<div class="mx-12 text-left">
|
||||
|
||||
{% if tema.score() is not none %}
|
||||
{% if tema.main_score() is not none %}
|
||||
{% set score = tema.main_score() %}
|
||||
{% include "fragments/tema/score.html" %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<div id="tema-score-{{ score.id }}">
|
||||
<h4 class="mp-4 text-xl text-beige">
|
||||
<i class="fa fa-code mr-2" aria-hidden="true"></i> Editor de partitura
|
||||
</h4>
|
||||
<h5 class="text-sm text-beige text-right">
|
||||
<input name="title"
|
||||
id="score-{{ score.id }}-editor-title"
|
||||
placeholder="Nom de la partitura"
|
||||
value="{{ score.title }}"
|
||||
class="border border-beige focus:outline-none
|
||||
rounded
|
||||
bg-brown px-2"
|
||||
/>
|
||||
<button title="Desa els canvis"
|
||||
class="mx-1"
|
||||
hx-put="/api/tema/{{ score.tema_id }}/score/{{ score.id }}"
|
||||
hx-vals="js:{title: document.getElementById('score-{{ score.id }}-editor-title').value, source: editor_{{ score.id }}.getValue()}"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-check" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button title="Descarta els canvis"
|
||||
class="mx-1"
|
||||
hx-get="/api/tema/{{ score.tema_id }}/score/{{ score.id }}"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h5>
|
||||
<h5 class="mp-4 text text-beige">
|
||||
Codi Lilypond
|
||||
<a href="https://lilypond.org/doc/v2.24/Documentation/learning/simple-notation"
|
||||
target="_blank">
|
||||
<i class="fa fa-info-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
|
||||
<div class="flex flex-col">
|
||||
<form class="flex flex-col"
|
||||
hx-post="/api/tema/{{ score.tema_id }}/editor/score/{{ score.id }}/render"
|
||||
hx-target="#score-{{ score.id }}-render-target"
|
||||
hx-swap="outerHTML"
|
||||
onsubmit="document.getElementById('lilypond-editor-{{ score.id }}').value = editor_{{ score.id }}.getValue()"
|
||||
>
|
||||
{% if score.source %}
|
||||
<textarea id="lilypond-editor-{{ score.id }}"
|
||||
name="source">{{ score.source | safe }}</textarea>
|
||||
{% else %}
|
||||
<textarea id="lilypond-editor-{{ score.id }}"
|
||||
name="source">{% include "lilypond/score_template.ly" %}</textarea>
|
||||
{% endif %}
|
||||
<script>
|
||||
var editor_{{ score.id }} = CodeMirror.fromTextArea(
|
||||
document.getElementById("lilypond-editor-{{ score.id }}"), {
|
||||
mode: "lilypond",
|
||||
lineNumbers: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<button title="Previsualitza els canvis"
|
||||
type="submit"
|
||||
class="flex flex-col items-center">
|
||||
<div class="m-2 mt-3 p-1 text-beige
|
||||
border border-beige rounded">
|
||||
Previsualitza els canvis
|
||||
</div>
|
||||
</button>
|
||||
</form>
|
||||
<div id="score-{{ score.id }}-render-target">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
<div id="score-{{ score_id }}-render-target">
|
||||
<h5 class="text text-beige"> Previsualització </h5>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
{% if score_render_url %}
|
||||
<img class="my-2" src="{{ score_render_url }}" />
|
||||
{% else %}
|
||||
<ol class="text-red-400">
|
||||
{% for error in errors %}
|
||||
<li class="text-red-500">Línia {{ error.line }} posició {{ error.pos }}: {{ error.error }}</p>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -4,12 +4,13 @@
|
||||
{% include "fragments/tema/title.html" %}
|
||||
</div>
|
||||
<div class="p-12 text-left">
|
||||
<div id="tema-{{ tema.id }}-score"
|
||||
hx-get="/api/tema/{{ tema.id }}/score"
|
||||
hx-trigger="load, reload-tema-{{ tema.id }}-score from:body"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
</div>
|
||||
<!-- <div id="tema-{{ tema.id }}-score" -->
|
||||
<!-- hx-get="/api/tema/{{ tema.id }}/score" -->
|
||||
<!-- hx-trigger="load, reload-tema-{{ tema.id }}-score from:body" -->
|
||||
<!-- hx-swap="innerHTML" -->
|
||||
<!-- > -->
|
||||
<!-- </div> -->
|
||||
{% include "fragments/tema/scores.html" %}
|
||||
{% include "fragments/tema/lyrics.html" %}
|
||||
{% include "fragments/tema/links.html" %}
|
||||
{% include "fragments/tema/properties.html" %}
|
||||
|
||||
@@ -1,15 +1,63 @@
|
||||
{% set score = tema.score() %}
|
||||
{% if score %}
|
||||
<h4 class="mp-4 text-xl text-beige">Partitura</h4>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
{% if score.link_type == LinkType.PDF %}
|
||||
{% set pdf_url = score.url %}
|
||||
{% include "fragments/pdf_viewer.html" %}
|
||||
{% elif score.link_type == LinkType.IMAGE %}
|
||||
<div class="flex justify-center">
|
||||
<a href="{{ score.url }}" target="_blank">
|
||||
<img class="m-2" src="{{ score.url }}" />
|
||||
</a>
|
||||
{% if logged_in or not score.hidden %}
|
||||
<div id="tema-score-{{ score.id }}">
|
||||
<h5 class="text-sm text-beige text-right">
|
||||
{{ score.title }}
|
||||
{% if logged_in and score.id is not none %}
|
||||
{% if score.source == "" %}
|
||||
<i>(Partitura buida)</i>
|
||||
{% elif score.errors %}
|
||||
<i>(Partitura amb errors)</i>
|
||||
{% endif %}
|
||||
{% if score.hidden %}
|
||||
<i>(Privada)</i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if logged_in and score.id is not none %}
|
||||
{% if score.hidden %}
|
||||
<button title="Mostra la partitura"
|
||||
class="mx-1"
|
||||
hx-put="/api/tema/{{ score.tema_id }}/score/{{ score.id }}/show"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% else %}
|
||||
<button title="Amaga la partitura"
|
||||
class="mx-1"
|
||||
hx-put="/api/tema/{{ score.tema_id }}/score/{{ score.id }}/hide"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<button title="Modifica la partitura"
|
||||
class="mx-1"
|
||||
hx-get="/api/tema/{{ score.tema_id }}/editor/score/{{ score.id }}"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button title="Esborra la partitura"
|
||||
class="mx-1"
|
||||
hx-delete="/api/tema/{{ score.tema_id }}/score/{{ score.id }}"
|
||||
hx-target="#tema-score-{{ score.id }}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
<div>
|
||||
{% if score.img_url is not none %}
|
||||
<div class="flex justify-center">
|
||||
<a href="{{ score.pdf_url or score.img_url }}" target="_blank">
|
||||
<img class="m-2" src="{{ score.img_url }}" />
|
||||
</a>
|
||||
</div>
|
||||
{% elif score.pdf_url is not none %}
|
||||
{% set pdf_url = score.pdf_url %}
|
||||
{% include "fragments/pdf_viewer.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
31
folkugat_web/assets/templates/fragments/tema/scores.html
Normal file
31
folkugat_web/assets/templates/fragments/tema/scores.html
Normal file
@@ -0,0 +1,31 @@
|
||||
{% set main_score = tema.main_score() %}
|
||||
{% if logged_in or tema.scores or main_score %}
|
||||
<h4 class="pt-4 text-xl text-beige">Partitures</h4>
|
||||
{% endif %}
|
||||
|
||||
{% if main_score is not none %}
|
||||
{% set score = main_score %}
|
||||
{% include "fragments/tema/score.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if tema.scores %}
|
||||
{% for score in tema.scores %}
|
||||
{% if score.id != main_score.id %}
|
||||
{% include "fragments/tema/score.html" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if logged_in %}
|
||||
<div id="new-score-target"></div>
|
||||
<div class="flex flex-row my-2 justify-end">
|
||||
<button title="Afegeix una partitura"
|
||||
class="text-sm text-beige text-right"
|
||||
hx-post="/api/tema/{{ tema.id }}/score"
|
||||
hx-target="#new-score-target"
|
||||
hx-swap="beforebegin">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
Afegeix una partitura
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -12,16 +12,5 @@
|
||||
hx-trigger="revealed, keyup delay:500ms changed"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML">
|
||||
{% if logged_in %}
|
||||
<button title="Afegeix un tema"
|
||||
class="text-beige m-2"
|
||||
hx-post="/api/tema"
|
||||
hx-include="[name=query]"
|
||||
hx-vals="js:{title: document.getElementById('query-input').value}"
|
||||
>
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
Afegeix un tema
|
||||
</button>
|
||||
{% endif %}
|
||||
<div id="search-results" class="flex-col"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
<div id="search-results"
|
||||
class="min-w-full w-full">
|
||||
{% if logged_in and query %}
|
||||
<button title="Afegeix un tema"
|
||||
class="border border-beige text-beige rounded
|
||||
m-1 px-2"
|
||||
hx-post="/api/tema"
|
||||
hx-include="[name=query]"
|
||||
hx-vals="js:{title: document.getElementById('query-input').value}"
|
||||
>
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
<i>{{ query }}</i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<table class="text-left min-w-full w-full">
|
||||
<tr class="border-b border-beige">
|
||||
<td class="font-bold py-2 px-4">Nom</td>
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
<!-- HTMX -->
|
||||
<script src="{{ url_for(request, 'static', path='js/htmx.min.js') }}"></script>
|
||||
|
||||
<!-- CodeMirror -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
|
||||
|
||||
<script src="{{ url_for(request, 'static', path='js/lilypond.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for(request, 'static', path='css/lilypond.css') }}" type="text/css" />
|
||||
|
||||
|
||||
<!-- Favicon! -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for(request, 'static', path='favicon/apple-touch-icon.png') }}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for(request, 'static', path='favicon/favicon-32x32.png') }}">
|
||||
|
||||
75
folkugat_web/assets/templates/lilypond/score.ly
Normal file
75
folkugat_web/assets/templates/lilypond/score.ly
Normal file
@@ -0,0 +1,75 @@
|
||||
\new ChordNames {
|
||||
\chords {
|
||||
\set chordChanges = ##t
|
||||
\set noChordSymbol = ""
|
||||
|
||||
\partial 2
|
||||
r2
|
||||
|
||||
% A
|
||||
\repeat volta 2 {
|
||||
d1 g a:5 r4 a2.:aug | d1 g a
|
||||
\alternative {
|
||||
\volta 1 { a4 a2.:aug }
|
||||
\volta 2 { d4 d2.:7 }
|
||||
}
|
||||
}
|
||||
|
||||
% B
|
||||
g1 d/fs g2 a b:m c/a
|
||||
g1 d/fs g2 a d a/cs
|
||||
|
||||
% C
|
||||
b1:m d/fs a g2 a
|
||||
b1:m d/fs a2 g d a/cs
|
||||
b1:m d/fs a g2 a
|
||||
b1:m g a2 a:aug a1:aug
|
||||
|
||||
}
|
||||
}
|
||||
\new Staff {
|
||||
\relative {
|
||||
\numericTimeSignature
|
||||
\time 4/4
|
||||
\set Timing.beamExceptions = #'()
|
||||
\set Timing.baseMoment = #(ly:make-moment 1/4)
|
||||
\set Timing.beatStructure = 2,2
|
||||
|
||||
\key d \major
|
||||
|
||||
\partial 2
|
||||
a8 b d e
|
||||
|
||||
% A
|
||||
\mark \default
|
||||
|
||||
\repeat volta 2 {
|
||||
fs4. fs8 e fs e d | g, r r4 e'8 fs e d |
|
||||
a16 b8( b8.) c16 b a8 b d e | e d( d) a( a) b d e |
|
||||
fs4. fs8 e fs e d | g, r r4 e'8 fs e d |
|
||||
a b d e e d( d) e |
|
||||
\alternative {
|
||||
\volta 1 { d4 a( a8) b d e }
|
||||
\volta 2 { d4 fs( fs) e8 d }
|
||||
}
|
||||
}
|
||||
|
||||
% B
|
||||
\mark \default
|
||||
|
||||
b2 e8 fs e d | a2 e'8 fs e d | b4. b8 a b d e | e d( d) fs( fs) a e d |
|
||||
b2 e8 fs e d | a2 e'8 fs e d | a b d e e d( d) e | d4 d16 d d8 d fs a cs |
|
||||
|
||||
\bar "||" \break
|
||||
|
||||
% C
|
||||
\mark \default
|
||||
|
||||
d4. d8 cs d cs a | fs4 e8 d a' b a fs | e2 e8 fs e d | a b d e d fs a cs |
|
||||
d4. d8 cs d cs b | fs4 e8 d a' b a fs | e2 d4. e8 | d4 d16 d d8 d fs a cs |
|
||||
d2 cs4 b8 a | fs4 e8 d a b a fs | e'2 e8 fs e d | a b d e d fs a cs |
|
||||
d2 d8 cs a fs | b2 b8 a fs d | e-> r r fs-> d-> r r4 | r8 c' c c c b a g |
|
||||
|
||||
\bar "|."
|
||||
}
|
||||
}
|
||||
61
folkugat_web/assets/templates/lilypond/score_template.ly
Normal file
61
folkugat_web/assets/templates/lilypond/score_template.ly
Normal file
@@ -0,0 +1,61 @@
|
||||
% -----------------------------------------------------
|
||||
% Comandes últils
|
||||
% -----------------------------------------------------
|
||||
% Anacrusa: \partial 2 (2 = blanca)
|
||||
%
|
||||
% Repetició amb caselles:
|
||||
% \repeat volta 2 {
|
||||
% %%%
|
||||
% \alternative {
|
||||
% \volta 1 { %%% }
|
||||
% \volta 2 { %%% }
|
||||
% }
|
||||
% }
|
||||
%
|
||||
% -----------------------------------------------------
|
||||
|
||||
\new ChordNames {
|
||||
\chords {
|
||||
\set chordChanges = ##t
|
||||
\set noChordSymbol = ""
|
||||
% -----------------------------------------------------
|
||||
% Acords
|
||||
% -----------------------------------------------------
|
||||
|
||||
% A
|
||||
|
||||
% B
|
||||
|
||||
}
|
||||
}
|
||||
\new Staff {
|
||||
\relative {
|
||||
% -----------------------------------------------------
|
||||
% Compàs
|
||||
% -----------------------------------------------------
|
||||
\numericTimeSignature
|
||||
\time 4/4
|
||||
\set Timing.beamExceptions = #'()
|
||||
\set Timing.baseMoment = #(ly:make-moment 1/4)
|
||||
\set Timing.beatStructure = 2,2
|
||||
|
||||
% -----------------------------------------------------
|
||||
% Armadura
|
||||
% -----------------------------------------------------
|
||||
\key c \major
|
||||
|
||||
% -----------------------------------------------------
|
||||
% Melodia
|
||||
% -----------------------------------------------------
|
||||
|
||||
% A
|
||||
\mark \default
|
||||
|
||||
\bar "||" \break
|
||||
|
||||
% B
|
||||
\mark \default
|
||||
|
||||
\bar "|."
|
||||
}
|
||||
}
|
||||
27
folkugat_web/assets/templates/lilypond/single_score.ly
Normal file
27
folkugat_web/assets/templates/lilypond/single_score.ly
Normal file
@@ -0,0 +1,27 @@
|
||||
\paper {
|
||||
top-margin = 10
|
||||
left-margin = 15
|
||||
right-margin = 15
|
||||
}
|
||||
|
||||
\header {
|
||||
title = \markup { "{{ tema.title }}" }
|
||||
{% if tema.composer() %}
|
||||
composer = "{{ tema.composer() }}"
|
||||
{% elif tema.origin() %}
|
||||
composer = "{{ tema.origin() }}"
|
||||
{% endif %}
|
||||
|
||||
tagline = "Partitura generada amb LilyPond"
|
||||
copyright = "Folkugat"
|
||||
}
|
||||
|
||||
\markup \vspace #2
|
||||
|
||||
\score {
|
||||
\language "english"
|
||||
<<
|
||||
{{ score_beginning }}
|
||||
{{ score_source | safe }}
|
||||
>>
|
||||
}
|
||||
Reference in New Issue
Block a user