aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wells <chris@mathematicaster.org>2024-01-24 14:56:56 -0600
committerChris Wells <chris@mathematicaster.org>2024-01-24 14:56:56 -0600
commitd6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6 (patch)
tree7ead1c89bf2490114129d51560280e29b174aaa6
downloadkobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar.gz
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar.bz2
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar.lz
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar.xz
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.tar.zst
kobo-manager-d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6.zip
Getting a whole kobo distribution together!
-rw-r--r--.gitignore1
-rw-r--r--README1
-rw-r--r--config/default.nix41
-rw-r--r--flake.lock60
-rw-r--r--flake.nix43
-rw-r--r--lib/default.nix14
-rw-r--r--modules/default.nix9
-rw-r--r--modules/installer.nix23
-rw-r--r--modules/kfmon.nix21
-rw-r--r--modules/kobo.nix61
-rw-r--r--modules/koreader.nix21
-rw-r--r--modules/plato.nix257
-rw-r--r--modules/redux/builder.nix11
-rw-r--r--modules/redux/file.nix44
-rw-r--r--modules/redux/kfmon.nix30
-rw-r--r--modules/redux/koreader.nix42
-rw-r--r--modules/redux/nickle.nix41
-rw-r--r--modules/redux/plato.nix179
-rw-r--r--overlays/default.nix7
-rw-r--r--pkgs/install.nix110
-rw-r--r--pkgs/plato.nix46
-rw-r--r--pkgs/source.nix21
22 files changed, 1083 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b2be92b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+result
diff --git a/README b/README
new file mode 100644
index 0000000..b9a8b29
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/kfmon.html
diff --git a/config/default.nix b/config/default.nix
new file mode 100644
index 0000000..4c9688a
--- /dev/null
+++ b/config/default.nix
@@ -0,0 +1,41 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+{
+ kfmon.enable = true;
+ koreader.enable = true;
+
+ plato = {
+ enable = true;
+
+ fonts.packages = [
+ pkgs.hack-font
+ ];
+
+ dictionaries.packages = with pkgs.dictdDBs; [
+ wordnet
+ wiktionary
+ ];
+
+ settings = {
+ keyboard-layout = "English";
+ frontlight = true;
+ wifi = false;
+ sleep-cover = true;
+ auto-share = true;
+ auto-suspend = 15.0;
+ auto-power-off = 4.0;
+ time-format = "%I:%M%P";
+ intermissions = {
+ suspend = "cover:";
+ power-off = "cover:";
+ share = "logo:";
+ };
+ reader = {
+ font-size = 10.0;
+ };
+ dictionary = {
+ font-size = 10.0;
+ };
+ };
+ };
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..0a95ed0
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,60 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1705309234,
+ "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1705608299,
+ "narHash": "sha256-Y//xAKx/uzpKXx8qSQfiur7rB386QJ7TVD3f0ByqJyM=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "68d14ba2a5c54ed931222079380f96de82489895",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..0a165b9
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,43 @@
+{
+ description = "Modules and packages to flash KOBO eReader firmware";
+
+ inputs = {
+ nixpkgs.url = github:nixos/nixpkgs;
+ flake-utils.url = github:numtide/flake-utils;
+ };
+
+ outputs = inputs@{ self, nixpkgs, flake-utils, ... }: {
+ lib = import ./lib { inherit (nixpkgs) lib; };
+
+ overlays.default = import ./overlays;
+
+ # koboConfigurations.default = self.lib.koboConfiguration {
+ # pkgs = import nixpkgs {
+ # system = "x86_64-linux";
+ # overlays = builtins.attrValues self.overlays;
+ # };
+ # modules = [ ./config ];
+ # };
+
+ } // flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = builtins.attrValues self.overlays;
+ };
+ myKobo = self.lib.koboConfiguration {
+ inherit pkgs;
+ modules = [ ./config ];
+ };
+ in {
+ packages = {
+ default = myKobo.config.kobo.package;
+ installer = myKobo.config.installer.package;
+ };
+
+ apps = {
+ type = "app";
+ program = "${pkgs.lib.getExe myKobo.config.installer.package} ${myKobo.config.kobo.package}";
+ };
+ });
+}
diff --git a/lib/default.nix b/lib/default.nix
new file mode 100644
index 0000000..fd028fb
--- /dev/null
+++ b/lib/default.nix
@@ -0,0 +1,14 @@
+{ lib, ... }:
+rec {
+ koboConfiguration = { pkgs, modules ? [], specialArgs ? {}, ... }: lib.evalModules {
+ specialArgs = { inherit pkgs; } // specialArgs;
+ modules = [
+ ../modules
+ ] ++ modules;
+ };
+
+ build = args: (koboConfiguration args).config.kobo.package;
+
+ installer = args: (koboConfiguration args).config.installer.package;
+
+}
diff --git a/modules/default.nix b/modules/default.nix
new file mode 100644
index 0000000..8e04cce
--- /dev/null
+++ b/modules/default.nix
@@ -0,0 +1,9 @@
+{
+ imports = [
+ ./installer.nix
+ ./kfmon.nix
+ ./kobo.nix
+ ./koreader.nix
+ ./plato.nix
+ ];
+}
diff --git a/modules/installer.nix b/modules/installer.nix
new file mode 100644
index 0000000..f5c93ba
--- /dev/null
+++ b/modules/installer.nix
@@ -0,0 +1,23 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.installer;
+in {
+ imports = [
+ ./kobo.nix
+ ];
+
+ options.installer = {
+ package = mkPackageOption pkgs [ "kobo" "install" ] // {
+ apply = x: x.override {
+ koboLabel = cfg.koboLabel;
+ };
+ };
+
+ koboLabel = mkOption {
+ type = types.str;
+ default = "KOBOeReader";
+ description = "The label of the Kobo eReader mount target";
+ };
+ };
+}
diff --git a/modules/kfmon.nix b/modules/kfmon.nix
new file mode 100644
index 0000000..f47433e
--- /dev/null
+++ b/modules/kfmon.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.kfmon;
+in {
+ options.kfmon = {
+ enable = mkEnableOption "koreader";
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to generate the KFMon config";
+ };
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/koreader";
+ description = "Releative path to KFMon within src";
+ };
+ };
+}
diff --git a/modules/kobo.nix b/modules/kobo.nix
new file mode 100644
index 0000000..d46da7e
--- /dev/null
+++ b/modules/kobo.nix
@@ -0,0 +1,61 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.kobo;
+in {
+ imports = [
+ ./kfmon.nix
+ ./koreader.nix
+ ./plato.nix
+ ];
+
+ options.kobo = {
+ package = mkOption {
+ type = types.package;
+ description = "The resulting kobo configuration";
+ default = if cfg.generate == "" then cfg.src else pkgs.stdenvNoCC.mkDerivation {
+ inherit (cfg) src;
+ name = "kobo-config";
+
+ dontBuild = true;
+ dontFixup = true;
+ dontConfigure = true;
+ dontPatch = true;
+
+ installPhase = ''
+ mkdir -p $out
+ cp -rv . $out
+
+ ${cfg.generate}
+ '';
+ };
+ };
+
+ src = mkOption {
+ type = types.package;
+ description = "The base source files to use";
+ default = with pkgs.kobo.src;
+ if config.plato.enable && config.koreader.enable then KOReader-Plato
+ else if config.plato.enable then Plato
+ else if config.koreader.enable then KOReader
+ else if config.kfmon.enable then KFMon
+ else error "At least one of plato, koreader or kfmon must be enabled in order to use a default package";
+ };
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to generate the kobo config";
+ };
+ };
+
+ config = {
+ kobo = {
+ generate = mkBefore (concatStringsSep "\n" (
+ optional config.kfmon.enable config.kfmon.generate ++
+ optional config.plato.enable config.plato.generate ++
+ optional config.koreader.enable config.koreader.generate
+ ));
+ };
+ };
+}
diff --git a/modules/koreader.nix b/modules/koreader.nix
new file mode 100644
index 0000000..b4b6551
--- /dev/null
+++ b/modules/koreader.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.koreader;
+in {
+ options.koreader = {
+ enable = mkEnableOption "koreader";
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to generate the KOReader config";
+ };
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/koreader";
+ description = "Releative path to KOReader within src";
+ };
+ };
+}
diff --git a/modules/plato.nix b/modules/plato.nix
new file mode 100644
index 0000000..afa7486
--- /dev/null
+++ b/modules/plato.nix
@@ -0,0 +1,257 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.plato;
+ toml = pkgs.formats.toml {};
+
+ bashList = concatMapStringsSep " " (x: ''"${toString x}"'');
+
+in {
+ options.plato = {
+ enable = mkEnableOption "plato";
+
+ # package = mkOption {
+ # type = types.package;
+ # readOnly = true;
+ # default = pkgs.runCommand "generate-plato" cfg.generate;
+ # };
+
+ # src = mkPackageOption pkgs [ "kobo" "plato" ] {};
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to generate the Plato config";
+ };
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/plato";
+ description = "Relative path to Plato within src";
+ };
+
+ hyphenation = mkEnableOption "hyphenation" // { default = true; };
+
+ config = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Plato config.sh";
+ };
+
+ settings = mkOption {
+ type = toml.type;
+ default = {};
+ description = "Plato Settings.toml";
+ };
+
+ dictionaries = {
+ packages = mkOption {
+ type = with types; listOf package;
+ default = [];
+ # default = with pkgs.dictdDBs; [
+ # wordnet
+ # wiktionary
+ # ];
+ description = "Dictionaries to install with plato";
+ };
+
+ sporadic = mkOption {
+ type = with types; listOf str;
+ default = [];
+ description = "Extra paths to dictionary files";
+ };
+
+ remove = mkOption {
+ type = with types; listOf str;
+ default = [];
+ description = "Remote these dictionaries from plato";
+ };
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to setup dictionaries";
+ };
+ };
+
+ fonts = {
+ packages = mkOption {
+ type = with types; listOf package;
+ default = [];
+ description = "Fonts to install for plato";
+ };
+
+ sporadic = mkOption {
+ type = with types; listOf str;
+ default = [];
+ description = "Extra paths to font files";
+ };
+
+ remove = mkOption {
+ type = with types; listOf str;
+ default = [];
+ description = "Remove these fonts from plato";
+ };
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to setup fonts";
+ };
+ };
+
+ css = {
+ epub = mkOption {
+ type = types.lines;
+ default = "";
+ description = "epub css";
+ };
+ html = mkOption {
+ type = types.lines;
+ default = "";
+ description = "html css";
+ };
+ dictionary = mkOption {
+ type = types.lines;
+ default = "";
+ description = "dictionary css";
+ };
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to setup css files";
+ };
+ };
+
+ wifi = {
+ up = {
+ pre = mkOption {
+ type = types.lines;
+ default = "";
+ };
+ post = mkOption {
+ type = types.lines;
+ default = "";
+ };
+ };
+ down = {
+ pre = mkOption {
+ type = types.lines;
+ default = "";
+ };
+ post = mkOption {
+ type = types.lines;
+ default = "";
+ };
+ };
+
+ generate = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Script used to setup wifi scripts";
+ };
+ };
+
+ };
+
+ config.plato = {
+ config = optionalString (!cfg.hyphenation) ''
+ [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns
+ '';
+
+ dictionaries.generate = mkBefore ''
+ mkdir -pv $out/${cfg.path}/dictionaries
+
+ ${optionalString (cfg.dictionaries.packages != []) ''
+ echo "Adding dictionaries from packages..."
+ for d in ${bashList cfg.dictionaries.packages}; do
+ find $d/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/${cfg.path}/dictionaries/ \;
+ done
+ ''}
+
+ ${optionalString (cfg.dictionaries.sporadic != []) ''
+ echo "Adding sporadic dictionaries..."
+ for p in ${bashList cfg.dictionaries.sporadic}; do
+ cp -v "$p" $out/${cfg.path}/dictionaries/
+ done
+ ''}
+
+ ${optionalString (cfg.dictionaries.remove != []) ''
+ echo "Removing unwanted dictionaries..."
+ for p in ${bashList cfg.dictionaries.remove}; do
+ rm -f "$out/${cfg.path}/dictionaries/$p"
+ done
+ ''}
+ '';
+
+ fonts.generate = mkBefore ''
+ mkdir -pv $out/${cfg.path}/fonts
+
+ ${optionalString (cfg.fonts.packages != []) ''
+ echo "Adding fonts from packages..."
+ for d in ${concatMapStringsSep " " toString cfg.fonts.packages}; do
+ find $d/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/${cfg.path}/fonts/ \;
+ done
+ ''}
+
+ ${optionalString (cfg.fonts.sporadic != []) ''
+ echo "Adding sporadic fonts"
+ for p in ${concatMapStringsSep " " (x: ''"${x}"'') cfg.fonts.sporadic}; do
+ cp -v "$p" $out/${cfg.path}/fonts/
+ done
+ ''}
+
+ ${optionalString (cfg.fonts.remove != []) ''
+ echo "Removing unwanted fonts..."
+ for p in ${bashList cfg.fonts.remove}; do
+ rm -f "$out/${cfg.path}/fonts/$p"
+ done
+ ''}
+ '';
+
+ css.generate = mkBefore ''
+ ${optionalString (cfg.css.epub != "") ''
+ cp -v ${writeText "epub-user.css" cfg.css.epub} $out/${cfg.path}/css/epub-user.css
+ ''}
+
+ ${optionalString (cfg.css.html != "") ''
+ cp -v ${pkgs.writeText "html-user.css" cfg.css.html} $out/${cfg.path}/css/html-user.css
+ ''}
+
+ ${optionalString (cfg.css.dictionary != "") ''
+ cp -v ${pkgs.writeText "dictionary-user.css" cfg.css.dictionary} $out/${cfg.path}/css/dictionary-user.css
+ ''}
+ '';
+
+ wifi.generate =
+ let
+ mkWifi = t: p: optionalString (cfg.wifi.${t}.${p} != "") ''
+ cp -v ${pkgs.writeScript "wifi-${p}-${t}.sh" "#!/bin/sh\n${cfg.wifi.${t}.${p}}"} $out/${cfg.path}/scripts/wifi-${p}-${t}.sh
+ '';
+ in mkBefore (
+ concatMapStringsSep "\n" ({ t, p }: mkWifi t p) (cartesianProductOfSets { t = [ "up" "down" ]; p = [ "pre" "post" ]; })
+ );
+
+
+ generate = mkBefore ''
+ mkdir -p $out/${cfg.path}
+
+ ${cfg.dictionaries.generate}
+
+ ${cfg.fonts.generate}
+
+ ${cfg.css.generate}
+
+ ${cfg.wifi.generate}
+
+ ${optionalString (cfg.settings != {}) ''
+ cp -v ${toml.generate "Settings.toml" cfg.settings} $out/${cfg.path}/Settings.toml
+ ''}
+
+ ${optionalString (cfg.config != "") ''
+ cp -v ${pkgs.writeScript "config.sh" cfg.config} $out/${cfg.path}/config.sh
+ ''}
+ '';
+ };
+}
diff --git a/modules/redux/builder.nix b/modules/redux/builder.nix
new file mode 100644
index 0000000..5f74a1e
--- /dev/null
+++ b/modules/redux/builder.nix
@@ -0,0 +1,11 @@
+{ pkgs, lib, ... }:
+with builtins // lib;
+{
+ copyFile = { source, directory, executable, target, ... }: ''
+ cp -${if directory then "r" else ""}v "${source}" "${target}"
+
+ ${optionalString (isBool executable) ''
+ chmod ${if executable then "+" else "-"}x "${target}"
+ ''}
+ '';
+}
diff --git a/modules/redux/file.nix b/modules/redux/file.nix
new file mode 100644
index 0000000..e05318d
--- /dev/null
+++ b/modules/redux/file.nix
@@ -0,0 +1,44 @@
+{ config, pkgs, lib, ... }:
+with builtins // lib;
+let
+ cfg = config.file;
+in {
+ options.file = mkOption {
+ default = {};
+ type = with types; lazyAttrsOf (submodule ({ name, config, ... }: {
+ options = {
+ enable = mkEnableOption name // { default = true; };
+ target = mkOption {
+ type = types.str;
+ default = name;
+ };
+ executable = mkOption {
+ type = with types; nullOr bool;
+ default = null;
+ };
+ directory = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable this if the source file is a directory";
+ };
+ source = mkOption {
+ type = types.path;
+ };
+ text = mkOption {
+ type = with types; nullOr lines;
+ default = null;
+ };
+ };
+
+ config = mkMerge [
+ (mkIf (config.text != null) {
+ directory = false;
+ source = pkgs.writeText name config.text;
+ })
+ (mkIf (config.text == "") {
+ enable = false;
+ })
+ ];
+ }));
+ };
+}
diff --git a/modules/redux/kfmon.nix b/modules/redux/kfmon.nix
new file mode 100644
index 0000000..772a10b
--- /dev/null
+++ b/modules/redux/kfmon.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, lib, options, ... }:
+with builtins // lib;
+let
+ cfg = config.kfmon;
+in {
+ options.kfmon = {
+ enable = mkEnableOption "KFMon";
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/kfmon";
+ description = "Relative path to kfmon within src";
+ };
+
+ file = mkOption {
+ type = options.file.type;
+ default = {};
+ description = "Files relative to ${cfg.path}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ file = mapAttrs' (n: v: {
+ name = "kfmon-${n}";
+ value = v // {
+ target = "${cfg.path}/${v.target}";
+ };
+ }) cfg.file
+ };
+}
diff --git a/modules/redux/koreader.nix b/modules/redux/koreader.nix
new file mode 100644
index 0000000..1c885a8
--- /dev/null
+++ b/modules/redux/koreader.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, lib, options, ... }:
+with builtins // lib;
+let
+ cfg = config.koreader;
+in {
+ options.koreader = {
+ enable = mkEnableOption "KOReader";
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/koreader";
+ description = "Relative path to KOReader within src";
+ };
+
+ nickleEntry = mkEnableOption "Launch KOReader directly via NickleMenu";
+
+ file = mkOption {
+ type = options.file.type;
+ default = {};
+ description = "Files relative to ${cfg.path}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ file = mapAttrs' (n: v: {
+ name = "koreader-${n}";
+ value = v // {
+ target = "${cfg.path}/${v.target}";
+ };
+ }) cfg.file
+
+ nickle.menuItems = mkIf cfg.nickleEntry {
+ "koreader" = [
+ "main"
+ "KOReader"
+ "cmd_spawn"
+ "quiet"
+ "exec /mnt/onboard/${cfg.path}/koreader.sh"
+ ];
+ };
+ };
+}
diff --git a/modules/redux/nickle.nix b/modules/redux/nickle.nix
new file mode 100644
index 0000000..b08a6ec
--- /dev/null
+++ b/modules/redux/nickle.nix
@@ -0,0 +1,41 @@
+{ config, pkgs, lib, options, ... }:
+with builtins // lib;
+let
+ cfg = config.nickle;
+in {
+ options.nickle = {
+ enable = mkEnableOption "NickleMenu";
+
+ path = mkOption {
+ type = types.str;
+ default = ".adds/nm";
+ description = "Relative path to NickleMenu within src";
+ };
+
+ file = mkOption {
+ type = options.file.type;
+ default = {};
+ description = "Files relative to ${cfg.path}";
+ };
+
+ menuItems = mkOption {
+ type = with types; attrsOf (listOf str);
+ default = {};
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nickle = {
+ file = mapAttrs (_: v: {
+ text = concatStringsSep " : " ([ "menu_item" ] ++ v)
+ }) cfg.menuItems
+ };
+
+ file = mapAttrs' (n: v: {
+ name = "nm-${n}";
+ value = v // {
+ target = "${cfg.path}/${v.target}";
+ };
+ }) cfg.file
+ };
+}
diff --git a/modules/redux/plato.nix b/modules/redux/plato.nix
new file mode 100644
index 0000000..6735133
--- /dev/null
+++ b/modules/redux/plato.nix
@@ -0,0 +1,179 @@
+{ config, pkgs, lib, options, ... }:
+with builtins // lib;
+let
+ cfg = config.plato;
+ toml = pkgs.formats.toml {};
+in {
+ imports = [
+ ./nickle.nix
+ ];
+
+ options.plato = {
+ enable = mkEnableOpiton "Plato";
+
+ path = mkOption {
+ type = types.srt;
+ default = ".adds/plato";
+ description = "Relative path to Plato within src";
+ };
+
+ file = mkOption {
+ type = options.file.type;
+ default = {};
+ description = "Files relative to ${cfg.path}";
+ };
+
+ nickleEntry = mkEnableOption "Launch Plato directly from NickleMenu";
+
+ config = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Plato config.sh";
+ };
+
+ hyphenation = mkEnableOption "hyphenation" // { default = true; };
+
+ convertDictionaries = mkEnableOption "convert StarDict" // { default = true; };
+
+ setFramebufferDepth = mkEnableOption "set framebuffer's depth" // { default = true; };
+
+ settings = mkOption {
+ type = toml.type;
+ default = {};
+ description = "Plato Settings.toml";
+ };
+
+ css = mkOption {
+ default = {};
+ type = with types; attrsOf lines;
+ description = ''
+ css style sheets.
+ Only use this for epub-user dictionary-user html-user
+ '';
+ };
+
+ scripts = mkOption {
+ default = {};
+ type = with types; attrsOf lines;
+ description = ''
+ Only use this for wifi-post-up wifi-post-down wifi-pre-up wifi-pre-down.
+ '';
+ # apply = mapAttrs (_: text: ''
+ # #!/bin/sh
+
+ # ${text}
+ # '')
+ };
+
+ dictionaries = mkOption {
+ type = with types; listOf package;
+ default = [];
+ };
+
+ allDictionaries = mkOption {
+ type = types.package;
+ readOnly = true;
+ internal = true;
+ default = pkgs.runCommand "PlatoDictionaries" {} (''
+ mkdir -p $out
+ '' + concatMapStringsSep "\n" (pkg: ''
+ find ${pkg}/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/ \;
+ '') cfg.dictionaries
+ );
+
+ };
+
+ fonts = mkOption {
+ type = with types; listOf package;
+ default = [];
+ };
+
+ allFonts = mkOption {
+ type = types.package;
+ readOnly = true;
+ internal = true;
+ default = pkgs.runCommand "PlatoFonts" {} (''
+ mkdir -p $out
+ '' + concatMapStringsSep "\n" (pkg: ''
+ find ${pkg}/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/ \;
+ '') cfg.fonts
+ );
+
+ };
+ };
+
+ config = mkIf cfg.enable {
+ plato = {
+ config = concatStringsSep "\n\n" (
+ optional (!cfg.convertDictionaries) ''
+ unset PLATO_CONVERT_DICTIONARIES
+ ''
+ ++
+ optional (!cfg.setFramebufferDepth) ''
+ unset PLATO_SET_FRAMEBUFFER_DEPTH
+ ''
+ ++
+ optional (!cfg.hyphenation) ''
+ [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns
+ ''
+ );
+ file = mkMerge [
+ (mkIf (cfg.settings != {}) {
+ "Settings.toml".source = toml.generate "Settings.toml" cfg.settings;
+ })
+
+ (mkIf (cfg.config != "") {
+ "config.sh".text = cfg.config;
+ })
+
+ (mapAttrs' (n: text:
+ let
+ n1 = if hasPrefix "css/" n then n else "css/${n}";
+ name = if hasSuffix ".css" n1 then n1 else "{n1}.css";
+ in {
+ inherit name;
+ value = { inherit text; }
+ }) cfg.css)
+
+ (mapAttrs' (n: t:
+ let
+ n1 = if hasPrefix "scripts/" n then n else "scripts/${n}";
+ name = if hasSuffix ".sh" n1 then n1 else "${n1}.sh";
+ text = if hasPrefix "#!/" t then t else "#!/bin/sh\n\n${t}";
+ in {
+ inherit name;
+ value = { inherit text; };
+ }) cfg.scripts)
+
+ {
+ dictionaries = {
+ directory = true;
+ source = cfg.allDictionaries;
+ };
+ fonts = {
+ directory = true;
+ source = cfg.allFonts;
+ };
+ }
+ ];
+ };
+
+ file = mapAttrs' (n: v: {
+ name = "plato-${n}";
+ value = v // {
+ target = "${cfg.path}/${v.target}";
+ };
+ }) cfg.file;
+
+ nickle.menuItems = mkIf cfg.nickleEntry {
+ "plato" = [
+ "main"
+ "Plato"
+ "cmd_spawn"
+ "quiet"
+ "exec /mnt/onboard/${cfg.path}/plato.sh"
+ ];
+ }
+
+ };
+}
diff --git a/overlays/default.nix b/overlays/default.nix
new file mode 100644
index 0000000..561e8c2
--- /dev/null
+++ b/overlays/default.nix
@@ -0,0 +1,7 @@
+final: prev: {
+ kobo = {
+ src = prev.callPackage ../pkgs/source.nix {};
+
+ install = prev.callpackage ../pkgs/install.nix {};
+ };
+}
diff --git a/pkgs/install.nix b/pkgs/install.nix
new file mode 100644
index 0000000..e25108d
--- /dev/null
+++ b/pkgs/install.nix
@@ -0,0 +1,110 @@
+{ writeShellApplication
+, coreutils
+, gnugrep
+, rsync
+, util-linux
+, koboLabel ? "KOBOeReader"
+, ... }: writeShellApplication {
+ name = "KoboInstaller";
+ runtimeInputs = [
+ coreutils
+ gnugrep
+ rsync
+ util-linux
+ ];
+ text = ''
+ INPUT=${1-}
+
+ if [[ -z "$INPUT" ]]; then
+ echo "Must provide an input directory"
+ exit 255
+ fi
+
+ if [[ ! -d "$INPUT/.kobo" ]] || [[ ! -d "$INPUT/.adds" ]]; then
+ echo "Missing one of $INPUT/.kobo or $INPUT/.adds. Are you sure your input directory is correct?"
+ exit 255
+ fi
+
+ KOBO_MOUNTPOINT="$(findmnt -nlo TARGET LABEL="${koboLabel}")"
+ if [[ -z "$KOBO_MOUNTPOINT" ]]; then
+ echo "Couldn't find a Kobo eReader volume! Is one actually mounted?"
+ exit 255
+ fi
+
+ KOBO_DIR="$KOBO_MOUNTPOINT/.kobo"
+ if [[ ! -d "$KOBO_DIR" ]]; then
+ echo "It doesn't seem like $KOBO_MOUNTPOINT actually points to a Kobo eReader since it doesn't contain a .kobo directory..."
+ exit 255
+ fi
+
+ # Tiny DRY helper for the failure message...
+ recap_failure() {
+ echo "* As a result, the device may have been left in an inconsistent state!"
+ echo "* Please note that NO automatic cleanup will be done!"
+ echo "* As such, before unplugging your device, consider removing:"
+ echo "* The following folders:"
+ for kfm_folder in .adds/kfmon .adds/koreader .adds/plato ; do
+ local current_folder="$KOBO_MOUNTPOINT/$kfm_folder"
+ if [[ -d "$current_folder" ]] ; then
+ printf "\t%s\n" "$current_folder"
+ else
+ printf "\t%-96s%32s\n" "$current_folder" "[OK: Already gone]"
+ fi
+ done
+ echo "* As well as the following files:"
+ for kfm_file in .kobo/KoboRoot.tgz kfmon.png koreader.png icons/plato.png ; do
+ local current_file="$KOBO_MOUNTPOINT/$kfm_file"
+ if [[ -f "$current_file" ]] ; then
+ printf "\t%s\n" "$current_file"
+ else
+ printf "\t%-96s%32s\n" "$current_file" "[OK: Already gone]"
+ fi
+ done
+ }
+
+ KOBO_CONFIG="$KOBO_DIR/Kobo/Kobo eReader.conf"
+ echo "* Preventing Nickel from scanning hidden directories . . ."
+ cat >> "${KOBO_CONFIG}" <<-\EoM
+
+ [FeatureSettings]
+ ExcludeSyncFolders=(\\.(?!kobo|adobe).+|([^.][^/]*/)+\\..+)
+ EoM
+
+ # Check for a cat failure, as unlikely as it might be (permissions?)...
+ ret=$?
+ if [[ $ret -ne 0 ]] ; then
+ echo "* Installation FAILED: Failed to update Nickel config!"
+ echo "* No permanent changes have been made."
+ exit $ret
+ fi
+
+ # Double-check that it was updated, in case of gremlins attack...
+ if ! grep -Fq 'ExcludeSyncFolders=(\\.(?!kobo|adobe).+|([^.][^/]*/)+\\..+)' "${KOBO_CONFIG}" ; then
+ echo "* Installation FAILED: Nickel config update was ineffective o_O !"
+ echo "* No permanent changes have been made."
+ exit 255
+ fi
+
+ echo "* Installing Kobo package"
+ rsync -vr "$INPUT/" "$KOBO_MOUNTPOINT"
+
+ ret=$?
+ if [[ $ret -ne 0 ]]; then
+ echo "* Installation FAILED: Failed to copy archive!"
+ recap_failure
+ exit $ret
+ fi
+
+ echo "* Sanity check . . ."
+ if [[ ! "$KOBO_DIR/KoboRoot.tgz" ]]; then
+ echo "* Installation FAILED: Copying was ineffective (no KoboRoot tarball) o_O !"
+ recap_failure
+ exit 255
+ fi
+
+ echo "* Installation successful!"
+ echo "* Please make sure to eject & unplug your device safely!"
+
+ exit 0
+ '';
+}
diff --git a/pkgs/plato.nix b/pkgs/plato.nix
new file mode 100644
index 0000000..815ac76
--- /dev/null
+++ b/pkgs/plato.nix
@@ -0,0 +1,46 @@
+{ stdenvNoCC
+, formats
+, runCommand
+, writeScript
+, lib
+
+, platoSrc
+, platoSettings ? {}
+, platoCfg ? ""
+, dictionaries ? []
+, fonts ? []
+, ... }:
+with builtins // lib;
+stdenvNoCC.mkDerivation (finalAttrs: {
+ name = "plato";
+
+ src = platoSrc;
+
+ dontFix = true;
+ dontPatch = true;
+ dontConfigure = true;
+ dontBuild = true;
+
+ installPhase = ''
+ mkdir -p $out
+ cp -rv . $out
+
+ # mkdir -p $out/.adds/plato/dictionaries
+ for d in ${concatMapStringsSep " " toString dictionaries}; do
+ find $d/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/.adds/plato/dictionaries/ \;
+ done
+
+ for d in ${concatMapStringsSep " " toString fonts}; do
+ find $d/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/.adds/plato/fonts/ \;
+ done
+
+ ${optionalString (platoSettings != {}) ''
+ cp -v ${(formats.toml {}).generate "Settings.toml" platoSettings} $out/.adds/plato/Settings.toml
+ ''}
+ ${optionalString (platoCfg != "") ''
+ cp -v ${writeScript "config.sh" platoCfg} $out/.adds/plato/config.sh
+ ''}
+ ''
+ ;
+
+})
diff --git a/pkgs/source.nix b/pkgs/source.nix
new file mode 100644
index 0000000..dcf40fe
--- /dev/null
+++ b/pkgs/source.nix
@@ -0,0 +1,21 @@
+{ fetchzip, ... }:
+{
+ KOReader = fetchzip {
+ stripRoot = false;
+ url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-KOReader-v2024.01.zip";
+ };
+ Plato = fetchzip {
+ stripRoot = false;
+ url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-Plato-0.9.40.zip";
+ sha256 = "sha256-jOCC3p9Q/DNtSADgMgmCwPvoZ4XA4ka1zH7dfg8r/kg=";
+ };
+ KOReader-Plato = fetchzip {
+ stripRoot = false;
+ url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-Plato-0.9.40_KOReader-v2024.01.zip";
+ sha256 = "sha256-A0ImPLQ+5/MLg5Iw3U3WnoKc2Cd/g6lSiSzAiBnrfZM=";
+ };
+ KFMon = fetchzip {
+ stripRoot = false;
+ url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-KFMon-1.4.6-43-g2c54087.zip";
+ };
+}