3 Commits

15 changed files with 215 additions and 89 deletions

View File

@@ -132,6 +132,7 @@ class IPUBBody extends IPUBElement {
IPUBImage,
IPUBInteraction,
IPUBSoundtrack,
IPUBTrigger,
]) {
console.info(`IPUBBody: Defining custom element <${e.elementName}>`);
globalThis.customElements.define(e.elementName, e);
@@ -282,6 +283,7 @@ class IPUBAudio extends IPUBElement {
}
if (this.#isFading) {
// TODO: Be able to force fading to be canceled
return;
}
@@ -553,7 +555,7 @@ class IPUBSoundtrack extends IPUBElement {
`IPUBSoundtrack: error while trying to play audio, error: ${e}`,
{
error: e,
audio: audio,
audio: last,
},
);
}
@@ -563,23 +565,32 @@ class IPUBSoundtrack extends IPUBElement {
* @private
*/
static #observer = (() => {
return new IntersectionObserver((entries) => {
for (const { intersectionRatio, target, time } of entries) {
/** @type {IPUBSoundtrack} */
const soundtrack = target;
return new IntersectionObserver(
(entries) => {
for (const { intersectionRatio, target, time } of entries) {
/** @type {IPUBSoundtrack} */
const soundtrack =
target.tagName === IPUBTrigger.elementName
? getAncestor(target, IPUBSoundtrack.elementName)
: target;
if (intersectionRatio > 0) {
console.debug(`${soundtrack.id} is on screen at ${time}`, soundtrack);
this.#onScreenStack.add(soundtrack);
} else {
console.debug(
`${soundtrack.id} is not on screen ${time}`,
soundtrack,
);
this.#onScreenStack.delete(soundtrack);
if (intersectionRatio === 1) {
console.debug(
`${soundtrack.id} is on screen at ${time}`,
soundtrack,
);
this.#onScreenStack.add(soundtrack);
} else {
console.debug(
`${soundtrack.id} is not on screen ${time}`,
soundtrack,
);
this.#onScreenStack.delete(soundtrack);
}
}
}
});
},
{ threshold: 1 },
);
})();
/**
@@ -637,10 +648,142 @@ class IPUBSoundtrack extends IPUBElement {
return;
}
IPUBSoundtrack.#observer.observe(this);
const trigger = this.querySelector(IPUBTrigger.elementName);
if (trigger) {
IPUBSoundtrack.#observer.observe(trigger);
} else {
IPUBSoundtrack.#observer.observe(this);
}
}
// TODO(guz013): Handle if element is moved, it's group should be updated
// TODO(guz013): Handle if element is deleted/disconnected, it should be removed from observer
}
class IPUBTrigger extends IPUBElement {
static elementName = "ipub-trigger";
static observedAttributes = ["height", "width"].concat(
IPUBElement.observedAttributes,
);
// TODO: Make this observer global
/** @private */
static #resizeObserver = new ResizeObserver((bodies) => {
for (const { target: body, contentRect } of bodies) {
const height = Math.max(body.scrollHeight, contentRect.height);
const width = Math.max(body.scrollWidth, contentRect.width);
for (const trigger of IPUBTrigger.#resizableTriggers.get(body)) {
const percH = trigger.getAttribute("height");
if (percH) {
trigger.style.setProperty(
"--ipub-height",
`${Math.round((height / 100) * Number.parseFloat(percH))}px`,
);
}
const percW = trigger.getAttribute("width");
if (percW) {
trigger.style.setProperty(
"--ipub-width",
`${Math.round((width / 100) * Number.parseFloat(percW))}px`,
);
}
}
}
});
/**
* @private
* @type {Map<IPUBBody, Set<IPUBTrigger>>}
*/
static #resizableTriggers = new Map();
// FIXME: trigger can be the same size as viewport, cap it to 80% of viewport
// height and 100% of viewport width
connectedCallback() {
super.connectedCallback();
const body = getAncestor(this, "ipub-body");
if (!body) {
console.error("IPUBTrigger: element must be a descendant of ipub-body");
return;
}
console.debug(
`IPUBTrigger#${this.id}: adding ${IPUBBody.elementName}#${body.id} from resize observer`,
);
IPUBTrigger.#resizeObserver.observe(body);
if (this.getAttribute("height") || this.getAttribute("width")) {
IPUBTrigger.#resizableTriggers.set(
body,
(IPUBTrigger.#resizableTriggers.get(body) || new Set()).add(this),
);
}
}
attributeChangedCallback(name, oldValue, newValue) {
super.attributeChangedCallback(name, oldValue, newValue);
const body = getAncestor(this, "ipub-body");
if (!body) {
console.error("IPUBTrigger: element must be a descendant of ipub-body");
return;
}
const set = IPUBTrigger.#resizableTriggers.get(body) || new Set();
if (this.getAttribute("height") || this.getAttribute("width")) {
set.add(this);
} else {
set.delete(this);
}
if (name === "width" || name === "height") {
const height = Math.max(
body.scrollHeight,
body.getBoundingClientRect().height,
);
const width = Math.max(
body.scrollWidth,
body.getBoundingClientRect().width,
);
const percH = this.getAttribute("height");
if (percH) {
this.style.setProperty(
"--ipub-height",
`${Math.round((height / 100) * Number.parseFloat(percH))}px`,
);
}
const percW = this.getAttribute("width");
if (percW) {
this.style.setProperty(
"--ipub-width",
`${Math.round((width / 100) * Number.parseFloat(percW))}px`,
);
}
}
}
disconnectedCallback() {
const set = IPUBTrigger.#resizableTriggers.get(body) || new Set();
set.delete(this);
if (set.size === 0) {
const body = getAncestor(this, "ipub-body");
if (!body) {
console.error("IPUBTrigger: element must be a descendant of ipub-body");
return;
}
console.debug(
`IPUBTrigger#${this.id}: removing ${IPUBBody.elementName}#${body.id} from resize observer`,
);
IPUBTrigger.#resizableTriggers.delete(body);
IPUBTrigger.#resizeObserver.unobserve(body);
}
}
}
/**

View File

@@ -28,6 +28,7 @@
<img src="../images/background0001.jpg" width="100" height="100" />
</ipub-background>
<ipub-soundtrack style="--ipub-color:cyan">
<ipub-trigger height="10" />
<!-- TODO: Search on how to make this more accessible, more semantic as using <details> -->
<figure>
<label>
@@ -57,6 +58,7 @@
<img src="../images/image0002.png" />
</ipub-image>
<ipub-soundtrack style="--ipub-color:green;">
<ipub-trigger height="10" />
<figure>
<label>
<input type="checkbox" />
@@ -90,6 +92,7 @@
<img src="../images/image0002.png" />
</ipub-image>
<ipub-soundtrack style="--ipub-color:yellow;">
<ipub-trigger height="10" />
<figure>
<label>
<input type="checkbox" />

View File

@@ -199,6 +199,18 @@ ipub-soundtrack {
&[playing] figure figcaption::before {
content: "P "; /* TODO: change to an icon and better positioning */
}
& > ipub-trigger {
position: absolute;
display: inline-block;
top: 0;
left: 0;
transform: translateY(--ipub-offset, 0%);
pointer-events: none;
width: var(--ipub-width, 100%);
height: var(--ipub-height, 0%);
}
}
ipub-background {

View File

@@ -1,17 +0,0 @@
# name: TODO tracker
# on: [push, pull_request]
# jobs:
# build:
# runs-on: ubuntu-latest
# steps:
# - uses: https://forge.capytal.company/actions/checkout@v3
# - uses: https://forge.capytal.company/actions/tdg-forgejo-action@master
# with:
# TOKEN: ${{ secrets.FORGEJO_TOKEN }}
# REPO: ${{ github.repository }}
# SHA: ${{ github.sha }}
# REF: ${{ github.ref }}
# LABEL: status/todo
# DRY_RUN: true
# COMMENT_ON_ISSUES: 128
# ASSIGN_FROM_BLAME: 128

1
.gitignore vendored
View File

@@ -3,5 +3,4 @@ out.css
.tmp
.env
*.db
*.epub
tmp

6
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1742069588,
"narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"lastModified": 1762844143,
"narHash": "sha256-SlybxLZ1/e4T2lb1czEtWVzDCVSTvk9WLwGhmxFmBxI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4",
"type": "github"
},
"original": {

View File

@@ -31,7 +31,7 @@
buildInputs = with pkgs; [
# Go tools
go
go_1_25
golangci-lint
gofumpt
gotools

5
go.mod
View File

@@ -1,6 +1,6 @@
module code.capytal.cc/capytal/comicverse
go 1.24.8
go 1.25.2
require (
code.capytal.cc/loreddev/smalltrip v0.0.0-20251013182121-3d201d21226c
@@ -10,7 +10,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/google/uuid v1.6.0
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92
golang.org/x/crypto v0.38.0
golang.org/x/crypto v0.43.0
)
require (
@@ -26,4 +26,5 @@ require (
github.com/aws/smithy-go v1.22.2 // indirect
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/sync v0.17.0 // indirect
)

6
go.sum
View File

@@ -38,11 +38,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92 h1:IYI1S1xt4WdQHjgVYzMa+Owot82BqlZfQV05BLnTcTA=
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92/go.mod h1:TjsB2miB8RW2Sse8sdxzVTdeGlx74GloD5zJYUC38d8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=

View File

@@ -1,7 +1,7 @@
go 1.24.8
go 1.25.2
use (
./.
.
./smalltrip
./x
)

View File

@@ -1,47 +1,16 @@
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2 h1:t/gZFyrijKuSU0elA5kRngP/oU3mc0I+Dvp8HwRE4c0=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1 h1:1M0gSbyP6q06gl3384wpoKPaH9G16NPqZFieEhLboSU=
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92 h1:IYI1S1xt4WdQHjgVYzMa+Owot82BqlZfQV05BLnTcTA=
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92/go.mod h1:TjsB2miB8RW2Sse8sdxzVTdeGlx74GloD5zJYUC38d8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=

17
repository/role.go Normal file
View File

@@ -0,0 +1,17 @@
package repository
import (
"context"
"database/sql"
"log/slog"
"code.capytal.cc/loreddev/x/tinyssert"
)
type RoleRepository struct {
db *sql.DB
ctx context.Context
log *slog.Logger
assert tinyssert.Assertions
}

1
templates/projects.html Normal file
View File

@@ -0,0 +1 @@
{{define "projects"}} {{end}}

2
x

Submodule x updated: 6ea200aa64...2ce5d71249