Compare commits
10 Commits
af1a1a9c2b
...
404a3971a3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
404a3971a3 | ||
![]() |
fc82c1a9ec | ||
![]() |
a25162dfa6 | ||
![]() |
7fd944d968 | ||
![]() |
eff03cfd43 | ||
![]() |
ad55d30a73 | ||
![]() |
3c63c04a0a | ||
![]() |
6343df000b | ||
![]() |
4985bb4b86 | ||
![]() |
1f88d1b76d |
28
.gitea/workflows/gitea-ci.yml
Normal file
28
.gitea/workflows/gitea-ci.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Jb-ssg-site CI Pipeline
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the code.
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Calculate short git commit SHA.
|
||||||
|
run: echo "SHORT_SHA=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV
|
||||||
|
- name: Build the Docker image.
|
||||||
|
run: docker build -t git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }} .
|
||||||
|
- name: Tag built image as latest.
|
||||||
|
run: docker tag git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }} git.jessebrault.com/jessebrault/jb-ssg-site:latest
|
||||||
|
- name: Login to Docker Registry.
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ vars.DOCKER_REGISTRY }}
|
||||||
|
username: ${{ vars.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
logout: true
|
||||||
|
- name: Publish short commit SHA image to Docker registry.
|
||||||
|
run: docker push git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }}
|
||||||
|
- name: Publish latest image tag to Docker registry.
|
||||||
|
run: docker push git.jessebrault.com/jessebrault/jb-ssg-site:latest
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
FROM eclipse-temurin:21 AS build
|
||||||
|
LABEL authors="jessebrault"
|
||||||
|
WORKDIR /jb-ssg-site/
|
||||||
|
COPY . .
|
||||||
|
RUN --mount=type=secret,id=JBARCHIVA_USERNAME,env=JBARCHIVA_USERNAME \
|
||||||
|
--mount=type=secret,id=JBARCHIVA_PASSWORD,env=JBARCHIVA_PASSWORD \
|
||||||
|
./gradlew createSsgBinScript && \
|
||||||
|
bin/ssg build -g && \
|
||||||
|
./gradlew installServerDist
|
||||||
|
|
||||||
|
FROM eclipse-temurin:21
|
||||||
|
LABEL authors="jessebrault"
|
||||||
|
WORKDIR /jb-ssg-site
|
||||||
|
COPY --from=build /jb-ssg-site/dist/ dist/
|
||||||
|
COPY --from=build /jb-ssg-site/static/ static/
|
||||||
|
COPY --from=build /jb-ssg-site/build/install/jb-ssg-site-server/ .
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["bin/JbServer"]
|
@ -10,18 +10,15 @@ import jakarta.inject.Named
|
|||||||
class Header extends BaseWebViewComponent {
|
class Header extends BaseWebViewComponent {
|
||||||
|
|
||||||
final String siteName
|
final String siteName
|
||||||
final String siteTagLine
|
|
||||||
final List<Page> menuItems
|
final List<Page> menuItems
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Header(
|
Header(
|
||||||
@Named('siteName') String siteName,
|
@Named('siteName') String siteName,
|
||||||
@Global('siteTagLine') String siteTagLine,
|
|
||||||
@Global('menuItems') List<String> menuItems,
|
@Global('menuItems') List<String> menuItems,
|
||||||
@InjectPages('/*') Set<Page> allPages
|
@InjectPages('/*') Set<Page> allPages
|
||||||
) {
|
) {
|
||||||
this.siteName = siteName
|
this.siteName = siteName
|
||||||
this.siteTagLine = siteTagLine
|
|
||||||
this.menuItems = menuItems.collect { menuItem ->
|
this.menuItems = menuItems.collect { menuItem ->
|
||||||
allPages.find { it.name == menuItem }
|
allPages.find { it.name == menuItem }
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,18 @@ package com.jessebrault.site
|
|||||||
|
|
||||||
import com.jessebrault.site.icon.BarsIcon
|
import com.jessebrault.site.icon.BarsIcon
|
||||||
---
|
---
|
||||||
<header id="header" data-turbo-permanent>
|
<header id="header">
|
||||||
<div class="titles">
|
<div class="titles">
|
||||||
<h1>$siteName</h1>
|
<h1>$siteName</h1>
|
||||||
<h2>$siteTagLine</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<nav>
|
<nav>
|
||||||
<button id="nav-bars" class="bars-wrapper">
|
<button id="nav-bars" class="bars-wrapper">
|
||||||
<BarsIcon />
|
<BarsIcon />
|
||||||
</button>
|
</button>
|
||||||
<ul id="nav-items">
|
<ul id="nav-items">
|
||||||
<Each items={menuItems} transform={<li><a class="nav-link" href={it.path}>$it.name</a></li>} />
|
<Each items={menuItems} transform={
|
||||||
|
<li><a class="nav-link" href={it.path}>$it.name</a></li>}
|
||||||
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
@ -9,10 +9,10 @@ import com.jessebrault.site.icon.YoutubeIcon
|
|||||||
<body>
|
<body>
|
||||||
<div class="header-banner-container">
|
<div class="header-banner-container">
|
||||||
<Header />
|
<Header />
|
||||||
<% children -> children << banner() %>
|
<Outlet children={[banner()]} />
|
||||||
</div>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<% children -> mainChildren.each { children << it } %>
|
<Outlet children={mainChildren} />
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<div class="social-icons">
|
<div class="social-icons">
|
||||||
@ -22,7 +22,6 @@ import com.jessebrault.site.icon.YoutubeIcon
|
|||||||
<p>Copyright $copyrightYear Jesse R. Brault. All rights reserved.</p>
|
<p>Copyright $copyrightYear Jesse R. Brault. All rights reserved.</p>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="/main.js"></script>
|
<script src="/main.js"></script>
|
||||||
<script src="turbo.es2017-esm.js" type="module"></script>
|
|
||||||
<Each items={pageScripts} transform={<script src={it}></script>} />
|
<Each items={pageScripts} transform={<script src={it}></script>} />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<div class="banner biography-banner">
|
<div class="banner biography-banner">
|
||||||
<div class="headshot-container">
|
<div class="headshot-container">
|
||||||
<img src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg" class="headshot" />
|
<img
|
||||||
|
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg"
|
||||||
|
class="headshot portrait-photo"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="banner-text blurb-container">
|
<div class="banner-text blurb-container">
|
||||||
<p id="jesse-brault-name">Jesse Brault</p>
|
<p id="jesse-brault-name">Jesse Brault</p>
|
||||||
|
@ -5,7 +5,10 @@ import com.jessebrault.site.icon.*
|
|||||||
---
|
---
|
||||||
<div class="banner contact-banner">
|
<div class="banner contact-banner">
|
||||||
<div class="headshot-container">
|
<div class="headshot-container">
|
||||||
<img src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg" class="headshot" />
|
<img
|
||||||
|
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg"
|
||||||
|
class="headshot portrait-photo"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<article class="banner-text social-media-links">
|
<article class="banner-text social-media-links">
|
||||||
<h1>Social Media</h1>
|
<h1>Social Media</h1>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="landscape-photo-container">
|
<div class="landscape-photo-container">
|
||||||
<img
|
<img
|
||||||
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/jesse-brault-bratislava.jpg"
|
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/jesse-brault-bratislava.jpg"
|
||||||
class="landscape-photo"
|
class="headshot landscape-photo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div id="lessons-banner-text" class="banner-text">
|
<div id="lessons-banner-text" class="banner-text">
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="headshot-container">
|
<div class="headshot-container">
|
||||||
<img
|
<img
|
||||||
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait4.jpg"
|
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait4.jpg"
|
||||||
class="headshot"
|
class="headshot portrait-photo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div id="videos-banner-text" class="banner-text">
|
<div id="videos-banner-text" class="banner-text">
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.jessebrault.site.developer
|
||||||
|
|
||||||
|
import com.jessebrault.site.util.TitleMaker
|
||||||
|
import com.jessebrault.ssg.di.InjectText
|
||||||
|
import com.jessebrault.ssg.di.SelfPage
|
||||||
|
import com.jessebrault.ssg.page.Page
|
||||||
|
import com.jessebrault.ssg.page.PageSpec
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import com.jessebrault.ssg.view.WvcPageView
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
|
||||||
|
@PageSpec(name = 'Developer', path = '/developer')
|
||||||
|
class DeveloperPage extends WvcPageView {
|
||||||
|
|
||||||
|
final Text developer
|
||||||
|
final Page selfPage
|
||||||
|
private final TitleMaker titleMaker
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DeveloperPage(
|
||||||
|
@InjectText('/Developer.md') Text developer,
|
||||||
|
@SelfPage Page selfPage,
|
||||||
|
TitleMaker titleMaker
|
||||||
|
) {
|
||||||
|
this.developer = developer
|
||||||
|
this.selfPage = selfPage
|
||||||
|
this.titleMaker = titleMaker
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTitle() {
|
||||||
|
titleMaker.makeTitle(pageTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDescription() {
|
||||||
|
developer.frontMatter.description
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,6 @@ package com.jessebrault.site.lessons
|
|||||||
|
|
||||||
import com.jessebrault.site.util.TitleMaker
|
import com.jessebrault.site.util.TitleMaker
|
||||||
import com.jessebrault.ssg.di.InjectText
|
import com.jessebrault.ssg.di.InjectText
|
||||||
import com.jessebrault.ssg.di.InjectTexts
|
|
||||||
import com.jessebrault.ssg.di.SelfPage
|
import com.jessebrault.ssg.di.SelfPage
|
||||||
import com.jessebrault.ssg.page.Page
|
import com.jessebrault.ssg.page.Page
|
||||||
import com.jessebrault.ssg.page.PageSpec
|
import com.jessebrault.ssg.page.PageSpec
|
||||||
@ -16,22 +15,22 @@ class LessonsPage extends WvcPageView {
|
|||||||
static final String description = 'Information about lessons offered by Jesse Brault.'
|
static final String description = 'Information about lessons offered by Jesse Brault.'
|
||||||
|
|
||||||
final Page selfPage
|
final Page selfPage
|
||||||
|
final Text lessons
|
||||||
private final TitleMaker titleMaker
|
private final TitleMaker titleMaker
|
||||||
private final Text lessonsText
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
LessonsPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Lessons.md') Text lessonsText) {
|
LessonsPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Lessons.md') Text lessons) {
|
||||||
this.selfPage = selfPage
|
this.selfPage = selfPage
|
||||||
this.titleMaker = titleMaker
|
this.titleMaker = titleMaker
|
||||||
this.lessonsText = lessonsText
|
this.lessons = lessons
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPageTitle() {
|
String getTitle() {
|
||||||
titleMaker.makeTitle(selfPage.name)
|
titleMaker.makeTitle(pageTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
String renderLessons() {
|
String renderLessons() {
|
||||||
this.lessonsText.render()
|
this.lessons.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
34
pages/groovy/com/jessebrault/site/resume/ResumePage.groovy
Normal file
34
pages/groovy/com/jessebrault/site/resume/ResumePage.groovy
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.jessebrault.site.resume
|
||||||
|
|
||||||
|
import com.jessebrault.site.util.TitleMaker
|
||||||
|
import com.jessebrault.ssg.di.InjectText
|
||||||
|
import com.jessebrault.ssg.di.SelfPage
|
||||||
|
import com.jessebrault.ssg.page.Page
|
||||||
|
import com.jessebrault.ssg.page.PageSpec
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import com.jessebrault.ssg.view.WvcPageView
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
|
||||||
|
@PageSpec(name = 'Résumé', path = '/resume')
|
||||||
|
class ResumePage extends WvcPageView {
|
||||||
|
|
||||||
|
final Text resume
|
||||||
|
final Page selfPage
|
||||||
|
private final TitleMaker titleMaker
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ResumePage(@InjectText('/Resume.md') Text resume, TitleMaker titleMaker, @SelfPage Page selfPage) {
|
||||||
|
this.resume = resume
|
||||||
|
this.titleMaker = titleMaker
|
||||||
|
this.selfPage = selfPage
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTitle() {
|
||||||
|
titleMaker.makeTitle(pageTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDescription() {
|
||||||
|
resume.frontMatter.description
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,6 @@ package com.jessebrault.site.videos
|
|||||||
|
|
||||||
import com.jessebrault.site.util.TitleMaker
|
import com.jessebrault.site.util.TitleMaker
|
||||||
import com.jessebrault.ssg.di.InjectText
|
import com.jessebrault.ssg.di.InjectText
|
||||||
import com.jessebrault.ssg.di.InjectTexts
|
|
||||||
import com.jessebrault.ssg.di.SelfPage
|
import com.jessebrault.ssg.di.SelfPage
|
||||||
import com.jessebrault.ssg.page.Page
|
import com.jessebrault.ssg.page.Page
|
||||||
import com.jessebrault.ssg.page.PageSpec
|
import com.jessebrault.ssg.page.PageSpec
|
||||||
@ -16,22 +15,22 @@ class VideosPage extends WvcPageView {
|
|||||||
static final String description = 'Conducting videos of Jesse Brault.'
|
static final String description = 'Conducting videos of Jesse Brault.'
|
||||||
|
|
||||||
final Page selfPage
|
final Page selfPage
|
||||||
|
final Text videos
|
||||||
private final TitleMaker titleMaker
|
private final TitleMaker titleMaker
|
||||||
private final Text videosText
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VideosPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Videos.md') Text videosText) {
|
VideosPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Videos.md') Text videos) {
|
||||||
this.selfPage = selfPage
|
this.selfPage = selfPage
|
||||||
this.titleMaker = titleMaker
|
this.titleMaker = titleMaker
|
||||||
this.videosText = videosText
|
this.videos = videos
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPageTitle() {
|
String getTitle() {
|
||||||
titleMaker.makeTitle(selfPage.name)
|
titleMaker.makeTitle(pageTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
String renderVideos() {
|
String renderVideos() {
|
||||||
videosText.render()
|
videos.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import com.jessebrault.site.util.OpenGraph
|
|||||||
>
|
>
|
||||||
<div class="article-container">
|
<div class="article-container">
|
||||||
<article id="biography-article">
|
<article id="biography-article">
|
||||||
<%= renderBiography() %>
|
<Render item={biography} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</StandardPage>
|
</StandardPage>
|
||||||
|
@ -65,7 +65,7 @@ import com.jessebrault.site.util.OpenGraph
|
|||||||
</WhenNotEmpty>
|
</WhenNotEmpty>
|
||||||
</section>
|
</section>
|
||||||
<section class="composition-text">
|
<section class="composition-text">
|
||||||
${composition.text.render()}
|
<Render item={composition.text} />
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
package com.jessebrault.site.developer
|
||||||
|
|
||||||
|
import com.jessebrault.site.StandardPage
|
||||||
|
import com.jessebrault.site.util.OpenGraph
|
||||||
|
---
|
||||||
|
<StandardPage
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
openGraph={<OpenGraph
|
||||||
|
title={pageTitle}
|
||||||
|
description={description}
|
||||||
|
path={selfPage.path}
|
||||||
|
/>}
|
||||||
|
>
|
||||||
|
<div class="article-container">
|
||||||
|
<article id="developer-article">
|
||||||
|
<Render item={developer} />
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</StandardPage>
|
@ -5,10 +5,10 @@ import com.jessebrault.site.StandardPage
|
|||||||
import com.jessebrault.site.util.OpenGraph
|
import com.jessebrault.site.util.OpenGraph
|
||||||
---
|
---
|
||||||
<StandardPage
|
<StandardPage
|
||||||
title={pageTitle}
|
title={title}
|
||||||
description={description}
|
description={description}
|
||||||
openGraph={<OpenGraph
|
openGraph={<OpenGraph
|
||||||
title={selfPage.name}
|
title={pageTitle}
|
||||||
description={description}
|
description={description}
|
||||||
path={selfPage.path}
|
path={selfPage.path}
|
||||||
/>}
|
/>}
|
||||||
@ -16,7 +16,7 @@ import com.jessebrault.site.util.OpenGraph
|
|||||||
>
|
>
|
||||||
<div class="article-container">
|
<div class="article-container">
|
||||||
<article id="lessons-article">
|
<article id="lessons-article">
|
||||||
<%= renderLessons() %>
|
<Render item={lessons} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</StandardPage>
|
</StandardPage>
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
package com.jessebrault.site.resume
|
||||||
|
|
||||||
|
import com.jessebrault.site.StandardPage
|
||||||
|
import com.jessebrault.site.util.OpenGraph
|
||||||
|
---
|
||||||
|
<StandardPage
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
openGraph={<OpenGraph
|
||||||
|
title={pageTitle}
|
||||||
|
description={description}
|
||||||
|
path={selfPage.path}
|
||||||
|
/>}
|
||||||
|
>
|
||||||
|
<div class="article-container">
|
||||||
|
<article id="resume-article">
|
||||||
|
<Render item={resume} />
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</StandardPage>
|
@ -5,10 +5,10 @@ import com.jessebrault.site.StandardPage
|
|||||||
import com.jessebrault.site.util.OpenGraph
|
import com.jessebrault.site.util.OpenGraph
|
||||||
---
|
---
|
||||||
<StandardPage
|
<StandardPage
|
||||||
title={pageTitle}
|
title={title}
|
||||||
description={description}
|
description={description}
|
||||||
openGraph={<OpenGraph
|
openGraph={<OpenGraph
|
||||||
title={selfPage.name}
|
title={pageTitle}
|
||||||
description={description}
|
description={description}
|
||||||
path={selfPage.path}
|
path={selfPage.path}
|
||||||
/>}
|
/>}
|
||||||
@ -16,7 +16,7 @@ import com.jessebrault.site.util.OpenGraph
|
|||||||
>
|
>
|
||||||
<div class="article-container">
|
<div class="article-container">
|
||||||
<article id="videos-article">
|
<article id="videos-article">
|
||||||
<%= renderVideos() %>
|
<Render item={videos} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</StandardPage>
|
</StandardPage>
|
||||||
|
@ -14,7 +14,7 @@ build {
|
|||||||
basePackage 'com.jessebrault.site'
|
basePackage 'com.jessebrault.site'
|
||||||
globals {
|
globals {
|
||||||
siteTagLine = 'Conductor, Composer, Developer'
|
siteTagLine = 'Conductor, Composer, Developer'
|
||||||
menuItems = ['Biography', 'Compositions', 'Videos', 'Lessons', 'Contact']
|
menuItems = ['Biography', 'Compositions', 'Videos', 'Lessons', 'Résumé', 'Developer', 'Contact']
|
||||||
compositionCategories = ['Orchestra', 'Chamber', 'Solo', 'Wind Ensemble']
|
compositionCategories = ['Orchestra', 'Chamber', 'Solo', 'Wind Ensemble']
|
||||||
spotifyUrl = 'https://open.spotify.com/artist/4ea1gZnLlQTrXKIMsnlr45?si=DfR-KCDyTiycbjmYwu566w'
|
spotifyUrl = 'https://open.spotify.com/artist/4ea1gZnLlQTrXKIMsnlr45?si=DfR-KCDyTiycbjmYwu566w'
|
||||||
youtubeUrl = 'https://www.youtube.com/@JesseBrault0709'
|
youtubeUrl = 'https://www.youtube.com/@JesseBrault0709'
|
||||||
|
@ -50,10 +50,6 @@ header .titles h1 {
|
|||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
header .titles h2 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav .bars-wrapper {
|
nav .bars-wrapper {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -130,6 +126,10 @@ article {
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article :is(h1, h2, h3) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
article h1 {
|
article h1 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
@ -143,7 +143,8 @@ article h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
article ul {
|
article ul {
|
||||||
padding-inline: 40px;
|
padding-left: 20px;
|
||||||
|
padding-right: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +202,7 @@ a:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headshot,
|
.portrait-photo,
|
||||||
.landscape-photo {
|
.landscape-photo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@ -481,13 +482,6 @@ form.contact {
|
|||||||
padding-inline: 30px;
|
padding-inline: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
header .titles h2 {
|
|
||||||
display: unset;
|
|
||||||
font-size: 2em;
|
|
||||||
font-size: 18px;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner,
|
.banner,
|
||||||
.landscape-banner {
|
.landscape-banner {
|
||||||
flex-direction: unset;
|
flex-direction: unset;
|
||||||
@ -512,6 +506,10 @@ form.contact {
|
|||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article ul {
|
||||||
|
padding-inline: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
flex-direction: unset;
|
flex-direction: unset;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -569,22 +567,6 @@ form.contact {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1023px) {
|
|
||||||
nav ul {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1;
|
|
||||||
width: 100%;
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
row-gap: 10px;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: var(--petrol);
|
|
||||||
border-bottom: 1px solid var(--charcoal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
header {
|
header {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -596,18 +578,6 @@ form.contact {
|
|||||||
letter-spacing: 0.175em;
|
letter-spacing: 0.175em;
|
||||||
}
|
}
|
||||||
|
|
||||||
header .titles h2 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav .bars-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner,
|
.banner,
|
||||||
.landscape-banner {
|
.landscape-banner {
|
||||||
padding: 0 50px 50px;
|
padding: 0 50px 50px;
|
||||||
@ -644,9 +614,29 @@ form.contact {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1199px) {
|
||||||
|
nav ul {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
row-gap: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--petrol);
|
||||||
|
border-bottom: 1px solid var(--charcoal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1200px) {
|
@media screen and (min-width: 1200px) {
|
||||||
header .titles h2 {
|
nav .bars-wrapper {
|
||||||
display: unset;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-banner svg {
|
.contact-banner svg {
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
|
const registerServiceWorker = async () => {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
try {
|
||||||
|
await navigator.serviceWorker.register('/serviceWorker.js', { scope: '/' })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Service worker registration failed: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerServiceWorker()
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
const navBars = document.getElementById('nav-bars')
|
const navBars = document.getElementById('nav-bars')
|
||||||
const navItems = document.getElementById('nav-items')
|
const navItems = document.getElementById('nav-items')
|
||||||
|
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
if (window.matchMedia('screen and (max-width: 1023px)').matches) {
|
if (window.matchMedia('screen and (max-width: 1199px)').matches) {
|
||||||
if (navItems.style.display === 'flex') {
|
if (navItems.style.display === 'flex') {
|
||||||
navItems.style.display = 'none'
|
navItems.style.display = 'none'
|
||||||
} else {
|
} else {
|
||||||
|
18
static/serviceWorker.js
Normal file
18
static/serviceWorker.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const addResourcesToCache = async () => {
|
||||||
|
const cache = await caches.open('v1')
|
||||||
|
cache.addAll(['/', '/compositions', '/videos', '/lessons', '/resume', '/developer', '/contact'])
|
||||||
|
;[
|
||||||
|
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg',
|
||||||
|
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg',
|
||||||
|
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait4.jpg',
|
||||||
|
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/jesse-brault-bratislava.jpg'
|
||||||
|
].forEach(async url => {
|
||||||
|
const request = new Request(url, { mode: 'no-cors' })
|
||||||
|
const response = await fetch(request)
|
||||||
|
cache.put(request, response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener('activate', event => {
|
||||||
|
event.waitUntil(addResourcesToCache())
|
||||||
|
})
|
File diff suppressed because it is too large
Load Diff
57
texts/Developer.md
Normal file
57
texts/Developer.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
description: Information about Java, TypeScript, and web developer Jesse Brault.
|
||||||
|
---
|
||||||
|
# Jesse Brault: Software Developer
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
- Java (5 years), Groovy (5 years), Kotlin (2 years)
|
||||||
|
- TypeScript (4 years), JavaScript (5 years), HTML, CSS/SCSS
|
||||||
|
- Lua, Ruby, Bash
|
||||||
|
|
||||||
|
## Frameworks, Libraries and Technologies
|
||||||
|
- Spring, Spring Boot, Gradle, JUnit, IntelliJ Idea
|
||||||
|
- NodeJS, React, Express, Prisma, GraphQL, VSCode
|
||||||
|
- MySQL, SQLite
|
||||||
|
|
||||||
|
## Programming Interests and Skills
|
||||||
|
- Web development
|
||||||
|
- Test-driven development
|
||||||
|
- Continuous integration and deployment
|
||||||
|
- REST protocol
|
||||||
|
- Dependency injection
|
||||||
|
- Programming languages
|
||||||
|
- Compilers
|
||||||
|
- Git, Github
|
||||||
|
- ORMs
|
||||||
|
- Open-source software
|
||||||
|
- Creating tools and plugins
|
||||||
|
|
||||||
|
## Personal Projects
|
||||||
|
All of my software projects are hosted publicly on GitHub. Please [contact](/contact) me if you have any inquiries related to them or my work in general.
|
||||||
|
|
||||||
|
### JVM
|
||||||
|
|
||||||
|
- [**groowt**, or **Groovy Web Tools**](https://github.com/JesseBrault0709/groowt): A set of libraries and CLI tools for facilitating web-development in Groovy, inspired by Ruby on Rails and Grails. Includes a custom templating language *Web View Components* inspired by ReactJS and Ruby's ERB templates, with a compiler developed with the ANTLR parser generator. 2024–present. Java, Groovy, Kotlin.
|
||||||
|
- [**ssg**, or **Static Site Generator**](https://github.com/JesseBrault0709/ssg): An extensible CLI application for statically generating websites from text files and other input. I created this out of a need for a static site generator in which I could embed Groovy without all the hassle of a full-size server-oriented framework such as Grails. So far, I am using it for all my personal sites (including this very site), and in the past has been used elsewhere. 2023–present. Groovy, Java.
|
||||||
|
- [**jbarchiva**](https://github.com/JesseBrault0709/jbarchiva): A Gradle plugin to facilitate easy access to my personal Maven repository at [archiva.jessebrault.com](https://archiva.jessebrault.com/). 2022-present.
|
||||||
|
- [**fsm**](https://github.com/JesseBrault0709/fsm): A set of easily configurable finite state machines with a simple domain-specific language, also featuring Groovy integration. Java, Groovy. 2022–2023.
|
||||||
|
|
||||||
|
### TypeScript
|
||||||
|
- [**jbci**, or **Jesse Brault Continuous Integration**](https://github.com/JesseBrault0709/jbci): A CI program written in TypeScript which listens to GitHub webhooks and then executes pre-configured shell scripts based on the webhook payload, mainly used to update my websites by simple pushes to my various GitHub repositories. 2022–present.
|
||||||
|
- [**jb-api**](https://github.com/JesseBrault0709/jb-api): A backend for my personal websites which listens for JSON "contact" requests, validates them, and sends mail to personal e-mail addresses. 2022–present.
|
||||||
|
- [**Meals Made Easy**](https://github.com/JesseBrault0709/MealsMadeEasy): A fully-functioning meal-planning web-app developed during my time in _Co.Lab | You Belong in Tech_. I worked closely with a product manager and designer to make the project come alive. It is built with TypeScript, React, Redux, and SCSS. 2021.
|
||||||
|
|
||||||
|
### Web Sites
|
||||||
|
- [Jesse Brault](https://github.com/JesseBrault0709/jb-ssg-site): Where you are right now. It is made using the above-described Groowt and Ssg libraries/utilities. 2024–present.
|
||||||
|
- [Red Dog Ensemble](https://github.com/JesseBrault0709/reddog): A website for a contemporary classical music ensemble I co-founded in New York City, made using the above-mentioned Ssg. 2023. <a href="https://reddogensemble.com/" target="_blank">Visit site.</a>
|
||||||
|
|
||||||
|
## Soft Skills
|
||||||
|
- Cooperation
|
||||||
|
- Team player
|
||||||
|
- Leadership
|
||||||
|
- Agile development method
|
||||||
|
- Self-discipline
|
||||||
|
- Quick learning
|
||||||
|
- Proactive spirit
|
||||||
|
- Cross-functional teamwork
|
||||||
|
- Embracing of team diversity
|
79
texts/Resume.md
Normal file
79
texts/Resume.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
description: Online résumé of conductor and composer Jesse Brault.
|
||||||
|
---
|
||||||
|
# Résumé
|
||||||
|
|
||||||
|
For an official up-to-date résumé/CV, please [contact](/contact) me directly.
|
||||||
|
|
||||||
|
## Education
|
||||||
|
- **Master of Music**, Orchestral Conducting. The Juilliard School. 2015–2018.
|
||||||
|
- Private Teachers: Alan Gilbert, James Ross
|
||||||
|
- Guest teachers: Bernard Haitink, Gianandrea Noseda, David Zinman, Fabio Luisi, James Gaffigan.
|
||||||
|
- Relevant coursework: score reading, counterpoint, harmony, history.
|
||||||
|
- **Bachelor of Music**, Theory and Composition. St. Olaf College. *Summa cum laude*. 2009–2013.
|
||||||
|
- Private Teachers: Steven Amundson, Timothy Mahr, Justin Merritt.
|
||||||
|
- Relevant coursework: composition, theory, history, analysis.
|
||||||
|
|
||||||
|
## Conducting Experience
|
||||||
|
- **Private Chamber Orchestra Concert**, at the Juilliard School. December 2023.
|
||||||
|
- Conducted own work *Spirit Travels*
|
||||||
|
- **NDR Elbphilharmonie Orchester**: Assistant conductor and off-stage brass conductor for Alan Gilbert. June 2022.
|
||||||
|
- Strauss: *An Alpine Symphony*
|
||||||
|
- **Boston Symphony Orchestra**: Assistant conductor for Yuan Chang. January 2020.
|
||||||
|
- Chihchun Chi-sun Lee: *Formosan Triptych*
|
||||||
|
- Mozart: Piano concerto no. 25
|
||||||
|
- Tchaikovsky: Symphony no. 3
|
||||||
|
- **New Jersey Symphony Orchestra**: Assistant conductor for Xian Zhang. December 2019.
|
||||||
|
- Anna Clyne: *Within Her Arms*
|
||||||
|
- Rachmaninoff: Piano concerto no. 3
|
||||||
|
- Dvořák: Symphony no. 8
|
||||||
|
- **St. Louis Symphony Orchestra**: Assistant conductor for Andrew Grams. November 2019.
|
||||||
|
- Tchaikovsky: *The Nutcracker*
|
||||||
|
- **Oedipus in the District**: Music director for a contemporary one-act opera performed twice at The Tank, New York, NY. October 2019.
|
||||||
|
- Josh Getman: *Oedipus in the District*
|
||||||
|
- **Red Dog Ensemble**: Conductor and co-founder. Inaugural recording of album *Neon and Oak* at Avaloch Farm Institute, Boscawen, New Hampshire. October 2019.
|
||||||
|
- Jon Cziner: *flowers of fire*
|
||||||
|
- Tom Morrison: *Neon and Oak*
|
||||||
|
- Sato Matsui: *The Imperfect Storm*
|
||||||
|
- Will Stackpole: *L'abîme*
|
||||||
|
- **Nord Anglia Performing Arts Festival**: Bratislava, Slovakia. Guest conductor. March 2019.
|
||||||
|
- Programmed music for two youth orchestras (ages 10–18) and a combined orchestra/chorus work
|
||||||
|
- Prepared parts for distribution to students in advance of festival
|
||||||
|
- Worked closely with festival staff during planning and execution stages
|
||||||
|
- Led and conducted all rehearsals and final concert
|
||||||
|
- **Ensemble Connect**: Guest conductor. March 2019.
|
||||||
|
- John Adams: *Chamber Symphony*
|
||||||
|
- **National Youth Symphony**: Prep-conductor for side-by-side with Daniel Harding and Royal Concertgebouw Orchestra.
|
||||||
|
February 2019.
|
||||||
|
- Schumann: *Manfred Overture*
|
||||||
|
- **Graduation Recital**, The Juilliard School. May 2018.
|
||||||
|
- Sibelius: *Valse triste*
|
||||||
|
- Mahler: Symphony no. 4 (arr. Stein)
|
||||||
|
- **Orlando Philharmonic**: Assistant conductor for Eric Jacobson. October 2017.
|
||||||
|
|
||||||
|
## Composition Performances
|
||||||
|
- *Spirit Travels*, at The Juilliard School. December 2023.
|
||||||
|
- *Capriccioso*, played on-air by NPO Klassiek (Netherlands). [Listen here at 24:20](https://www.npoklassiek.nl/uitzendingen/podium/cb3c4d56-6c59-4100-8a92-b6081daf9719/2022-03-19-podium)
|
||||||
|
- *Capriccioso*, at the Mauritshuis, The Hague, Netherlands. Recorded February 2022 and released live in March 2022.
|
||||||
|
- *Near, Under, Far*, online performance by cellist Gabriel Cabezas from New York, New York. October 2021.
|
||||||
|
- *Arcadia*, online performance by Julia Hamos and Stephen Waarts from Berlin, Germany. November 2020.
|
||||||
|
- *Conversations*, performed by Jocelyn Zhu and Mariella Haubs on WQXR, New York, New York. April 2019.
|
||||||
|
- *Conversations*, world premiere in New York, New York by Jocelyn Zhu and Mariella Haubs. April 2019.
|
||||||
|
- *Sonata Shambhala*, premieres at Marlboro College, Vermont; Seattle, Washington; and Camphill Ghent, New York;
|
||||||
|
by Benjamin Hochman. November 2018.
|
||||||
|
|
||||||
|
## Album
|
||||||
|
- **Neon and Oak**: recorded with [Red Dog Ensemble](https://reddogensemble.com/) and released in March 2023. Co-edited with Will Stackpole. Features four new works by emerging composers. See [here](https://reddogensemble.com/album.html) for more information.
|
||||||
|
|
||||||
|
## Teaching Experience
|
||||||
|
- **Juilliard Pre-College**: Substitute teacher for music theory, counterpoint, and ear training. 2018–2020.
|
||||||
|
- **Fordham University**: Adjunct professor and Fordham Lincoln Center Chamber Orchestra conductor. 2018–2019.
|
||||||
|
- **Juilliard Music Advancement Program**: Substitute teacher for music theory, ear training, and history. 2018–2019.
|
||||||
|
- **New York Youth Symphony**: Substitute teacher for private composition lessons. 2019.
|
||||||
|
- **St. Olaf College**: Teaching assistant for Steven Amundson, music theory. 2010–2013.
|
||||||
|
- **Private Tutor**, St. Olaf College: for ear training. 2011–2013.
|
||||||
|
|
||||||
|
## Instruments
|
||||||
|
- Trombone
|
||||||
|
- Piano
|
||||||
|
- Organ
|
Loading…
Reference in New Issue
Block a user