aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--LICENSE24
-rw-r--r--README.md38
-rwxr-xr-xbin/dev-server21
-rwxr-xr-xbin/get-data23
-rwxr-xr-xbin/view16
-rw-r--r--flake.lock42
-rw-r--r--flake.nix20
-rw-r--r--public/index.html6
-rw-r--r--public/main.css21
-rw-r--r--public/main.js19
11 files changed, 231 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e34fb35
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+public/data.js
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fdddb29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to <https://unlicense.org>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e7e6c8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# Books
+
+Visualize a book library.
+
+## Book library
+
+Organize folders as you wish, only `metadata.json` files matter:
+
+```json
+{
+ "title": "Title of the Book",
+ "subtitle": "Optional subtitle",
+ "authors": [ "Author 1", "Author 2" ],
+ "authorsSort": "Author sorting",
+ "genres": [ "Foo", "Bar", "Baz" ],
+ "year": "1234",
+ "read": true
+}
+```
+
+Each `metadata.json` file correspond to a book, and there **must** be a cover
+named `cover.ext` in the same directory. Any extension works.
+
+## Show library
+
+View the book library by running:
+
+ ./bin/view browser-cmd path-to-books
+
+## Dev server
+
+Enter nix shell:
+
+ nix develop --command ./watch-command
+
+Then run the dev-server:
+
+ ./bin/dev-server path-to-books
diff --git a/bin/dev-server b/bin/dev-server
new file mode 100755
index 0000000..4136091
--- /dev/null
+++ b/bin/dev-server
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd $(dirname "$0")/..
+
+if [ "$#" == 1 ]; then
+ BOOK_DIR="$1"
+else
+ echo "usage: $0 path-to-book-directory"
+ exit 1
+fi
+
+# Watch
+
+clear
+echo "Open your browser at file://$PWD/public/index.html"
+echo
+
+BUILD_CMD="./bin/get-data $BOOK_DIR > public/data.js && echo public/data.js updated."
+watchexec \
+ --watch "$BOOK_DIR" \
+ -- "$BUILD_CMD"
diff --git a/bin/get-data b/bin/get-data
new file mode 100755
index 0000000..0fbbd2f
--- /dev/null
+++ b/bin/get-data
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd $(dirname "$0")/..
+
+if [ "$#" == 1 ]; then
+ BOOK_DIR="$1"
+else
+ echo "usage: $0 path-to-book-directory"
+ exit 1
+fi
+
+echo "const books = ["
+
+for METADATA in $(find "$BOOK_DIR" -name 'metadata.json'); do
+ DIR=$(dirname "$METADATA")
+ COVER=$(ls $DIR/cover.*)
+
+ cat "$METADATA" | head -n -1
+ echo ", \"cover\": \"$COVER\""
+ echo "},"
+done
+
+echo "]"
diff --git a/bin/view b/bin/view
new file mode 100755
index 0000000..159bc6a
--- /dev/null
+++ b/bin/view
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd $(dirname "$0")/..
+
+if [ "$#" == 2 ]; then
+ BROWSER="$1"
+ BOOK_DIR="$2"
+else
+ echo "usage: $0 browser-cmd path-to-book-directory"
+ exit 1
+fi
+
+bin/get-data "$BOOK_DIR" > "public/data.js"
+TMP_DIR=$(mktemp --directory)
+cp public/* "$TMP_DIR"
+eval "$BROWSER $TMP_DIR/index.html"
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..4775dd0
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,42 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1667395993,
+ "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1674290375,
+ "narHash": "sha256-yRD5bQWzu6UpDC6cybXwFizWHJCHcJTw35Ms7Q/nzU4=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "68403fe04f6c85853ddd389c9e58dd9b9c8b0a36",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..d2e3a07
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,20 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs";
+ flake-utils.url = "github:numtide/flake-utils";
+ };
+
+ outputs = { self, nixpkgs, flake-utils, ... }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs { inherit system; };
+ in with pkgs; {
+ devShell = mkShell {
+ buildInputs = [
+ psmisc
+ watchexec
+ ];
+ };
+ }
+ );
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..13136da
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="stylesheet" href="main.css">
+<body></body>
+<script src="data.js"></script>
+<script src="main.js"></script>
diff --git a/public/main.css b/public/main.css
new file mode 100644
index 0000000..711aaf2
--- /dev/null
+++ b/public/main.css
@@ -0,0 +1,21 @@
+body {
+ margin: 2rem;
+}
+
+.g-Books {
+ display: grid;
+ grid-template-columns: repeat(7, minmax(0, 1fr));
+ grid-gap: 1rem;
+
+}
+
+.g-Book {
+ width: 13rem;
+ align-self: center;
+ border: 1px solid #DDDDDD;
+}
+
+.g-Book:hover {
+ transform: scale(1.1);
+ transition: transform 0.1s linear;
+}
diff --git a/public/main.js b/public/main.js
new file mode 100644
index 0000000..29de235
--- /dev/null
+++ b/public/main.js
@@ -0,0 +1,19 @@
+const sortedBooks = books.sort((a, b) =>
+ a.authorsSort == b.authorsSort
+ ? a.date > b.date
+ : a.authorsSort > b.authorsSort)
+
+const view = h('div',
+ { className: 'g-Books' },
+ ...sortedBooks.map(book => h('img', { className: 'g-Book', src: book.cover })))
+
+document.body.appendChild(view)
+
+// Helpers
+
+function h(tagName, attrs, ...children) {
+ let elem = document.createElement(tagName)
+ elem = Object.assign(elem, attrs)
+ for (const child of children) elem.append(child)
+ return elem
+}