Compare commits

...

31 Commits

Author SHA1 Message Date
Alex Ling 1fb48648ad Merge pull request #322 from getmango/rc/0.27.0
v0.27.0
2022-07-31 22:53:11 +08:00
Alex Ling 7ceb91f051 Merge branch 'rc/0.27.0' into dev 2022-07-31 13:55:02 +00:00
Alex Ling 9ea4ced729 Merge pull request #327 from phlhg/fix/static-manifest
Fix for Error 404 on manifest.json
2022-07-31 21:54:06 +08:00
Alex Ling 4c2f802e2e Fix linter 2022-07-31 10:19:21 +00:00
Philippe Hugo 7258b3cece Add /manifest.json to static files 2022-07-27 17:59:11 +02:00
Alex Ling bf885a8b30 Bump version to 0.27.0 2022-07-18 12:38:22 +00:00
Alex Ling 98a0c54499 Merge pull request #311 from hkalexling/fix/hide-subscribe-btn
Hide subscribe btn
2022-07-18 20:03:10 +08:00
Alex Ling cb3df432d0 Merge branch 'dev' into fix/hide-subscribe-btn 2022-07-18 19:42:23 +08:00
Alex Ling 47af6ee284 Merge pull request #321 from hkalexling/fix/plugin-use-html-parser
Use html parser in plugin helper functions
2022-07-18 19:41:40 +08:00
Alex Ling 9fe269ab13 Disable plugin_spec.cr line limit 2022-07-17 15:24:01 +00:00
Alex Ling 75a30a88e0 Use myhtml in plugin helper and add tests (#320) 2022-07-17 14:54:25 +00:00
Alex Ling 5daeac72cb Merge pull request #317 from Hiers/feature/image-fit
Fit image options
2022-07-17 11:55:35 +08:00
Hiers dc3ac42dec Right flip panels are 1/3 of the rightmost area of the entire screen, not of the page. (same for left flip panels) 2022-07-16 12:09:23 +01:00
Hiers 624283643c Fixed right flip panel not being all the way on the right; changed real image size option to not be hard coded. 2022-07-13 14:20:43 +01:00
Hiers 6ddbe8d436 Changed setFit function to not have redundant ifs and a better comment explaining what it does. 2022-07-07 08:55:54 +01:00
Hiers db5e99b3f0 Fix in reader.html.ecr. 2022-07-05 22:24:31 +01:00
Hiers 405b958deb First draft of image fit. 2022-07-05 22:01:21 +01:00
Alex Ling e7c4123dec Merge pull request #315 from Leeingnyo/fix/rescan-when-files-added
Fix Dir.contents_signature to detect valid image files
2022-07-03 15:59:49 +08:00
Alex Ling 2d2486a598 Merge branch 'dev' into fix/rescan-when-files-added 2022-07-03 15:44:02 +08:00
Alex Ling b6a1ad889e Merge pull request #314 from crainte/feature/default-env-vars
Allow config defaults to be sourced from ENV
2022-07-03 15:39:47 +08:00
Alex Ling f2d6d28a72 Define properties with macro 2022-07-03 07:24:33 +00:00
Alex Ling 49425ff714 Merge branch 'feature/default-env-vars' of https://github.com/crainte/Mango into feature/default-env-vars 2022-07-03 06:31:06 +00:00
Chris Alexander f3eb62a271 Disable line length warnings 2022-06-27 09:30:04 -05:00
Chris Alexander 2e91028ead Allow config defaults to be sourced from ENV
This allows the default config to source values from ENV variables if
they are set. With this change we don't have to modify the docker CMD or
edit the config.yml and then relaunch.
2022-06-27 09:30:04 -05:00
Alex Ling 19a8f3100b Merge branch 'dev' into fix/rescan-when-files-added 2022-06-26 11:52:15 +08:00
Leeingnyo 17a9c8ecd3 pass lint 2022-06-18 18:51:33 +09:00
Leeingnyo bbc0c2cbb7 Fix Dir.contents_signature to detect valid image files added 2022-06-18 17:43:57 +09:00
Chris Alexander be46dd1f86 Allow config defaults to be sourced from ENV
This allows the default config to source values from ENV variables if
they are set. With this change we don't have to modify the docker CMD or
edit the config.yml and then relaunch.
2022-06-15 12:03:40 -05:00
Alex Ling 5b58d8ac59 Clear page when switching plugins 2022-06-05 12:40:45 +00:00
Alex Ling 30d5ad0c19 Hide subscribe button when not subscribable 2022-06-05 12:33:26 +00:00
Alex Ling 61dc92838a Merge pull request #294 from hkalexling/rc/0.26.2
v0.26.2
2022-04-18 18:46:11 +08:00
19 changed files with 233 additions and 55 deletions
+1
View File
@@ -12,3 +12,4 @@ Layout/LineLength:
MaxLength: 80 MaxLength: 80
Excluded: Excluded:
- src/routes/api.cr - src/routes/api.cr
- spec/plugin_spec.cr
+1 -1
View File
@@ -51,7 +51,7 @@ The official docker images are available on [Dockerhub](https://hub.docker.com/r
### CLI ### CLI
``` ```
Mango - Manga Server and Web Reader. Version 0.26.2 Mango - Manga Server and Web Reader. Version 0.27.0
Usage: Usage:
+6
View File
@@ -1,6 +1,7 @@
const component = () => { const component = () => {
return { return {
plugins: [], plugins: [],
subscribable: false,
info: undefined, info: undefined,
pid: undefined, pid: undefined,
chapters: undefined, // undefined: not searched yet, []: empty chapters: undefined, // undefined: not searched yet, []: empty
@@ -60,6 +61,7 @@ const component = () => {
.then((data) => { .then((data) => {
if (!data.success) throw new Error(data.error); if (!data.success) throw new Error(data.error);
this.info = data.info; this.info = data.info;
this.subscribable = data.subscribable;
this.pid = pid; this.pid = pid;
}) })
.catch((e) => { .catch((e) => {
@@ -70,6 +72,9 @@ const component = () => {
}); });
}, },
pluginChanged() { pluginChanged() {
this.manga = undefined;
this.chapters = undefined;
this.mid = undefined;
this.loadPlugin(this.pid); this.loadPlugin(this.pid);
localStorage.setItem("plugin", this.pid); localStorage.setItem("plugin", this.pid);
}, },
@@ -140,6 +145,7 @@ const component = () => {
if (!query) return; if (!query) return;
this.manga = undefined; this.manga = undefined;
this.mid = undefined;
if (this.info.version === 1) { if (this.info.version === 1) {
this.searchChapters(query); this.searchChapters(query);
} else { } else {
+11
View File
@@ -14,6 +14,7 @@ const readerComponent = () => {
margin: 30, margin: 30,
preloadLookahead: 3, preloadLookahead: 3,
enableRightToLeft: false, enableRightToLeft: false,
fitType: 'vert',
/** /**
* Initialize the component by fetching the page dimensions * Initialize the component by fetching the page dimensions
@@ -65,6 +66,11 @@ const readerComponent = () => {
this.preloadImage(this.items[idx - 1].url); this.preloadImage(this.items[idx - 1].url);
} }
const savedFitType = localStorage.getItem('fitType');
if (savedFitType) {
this.fitType = savedFitType;
$('#fit-select').val(savedFitType);
}
const savedFlipAnimation = localStorage.getItem('enableFlipAnimation'); const savedFlipAnimation = localStorage.getItem('enableFlipAnimation');
this.enableFlipAnimation = savedFlipAnimation === null || savedFlipAnimation === 'true'; this.enableFlipAnimation = savedFlipAnimation === null || savedFlipAnimation === 'true';
@@ -335,6 +341,11 @@ const readerComponent = () => {
this.toPage(this.selectedIndex); this.toPage(this.selectedIndex);
}, },
fitChanged(){
this.fitType = $('#fit-select').val();
localStorage.setItem('fitType', this.fitType);
},
preloadLookaheadChanged() { preloadLookaheadChanged() {
localStorage.setItem('preloadLookahead', this.preloadLookahead); localStorage.setItem('preloadLookahead', this.preloadLookahead);
}, },
+1 -1
View File
@@ -1,5 +1,5 @@
name: mango name: mango
version: 0.26.2 version: 0.27.0
authors: authors:
- Alex Ling <hkalexling@gmail.com> - Alex Ling <hkalexling@gmail.com>
View File
+6
View File
@@ -0,0 +1,6 @@
{
"id": "test",
"title": "Test Plugin",
"placeholder": "placeholder",
"wait_seconds": 1
}
+19 -2
View File
@@ -1,14 +1,31 @@
require "./spec_helper" require "./spec_helper"
describe Config do describe Config do
it "creates config if it does not exist" do it "creates default config if it does not exist" do
with_default_config do |_, path| with_default_config do |config, path|
File.exists?(path).should be_true File.exists?(path).should be_true
config.port.should eq 9000
end end
end end
it "correctly loads config" do it "correctly loads config" do
config = Config.load "spec/asset/test-config.yml" config = Config.load "spec/asset/test-config.yml"
config.port.should eq 3000 config.port.should eq 3000
config.base_url.should eq "/"
end
it "correctly reads config defaults from ENV" do
ENV["LOG_LEVEL"] = "debug"
config = Config.load "spec/asset/test-config.yml"
config.log_level.should eq "debug"
config.base_url.should eq "/"
end
it "correctly handles ENV truthiness" do
ENV["CACHE_ENABLED"] = "false"
config = Config.load "spec/asset/test-config.yml"
config.cache_enabled.should be_false
config.cache_log_enabled.should be_true
config.disable_login.should be_false
end end
end end
+70
View File
@@ -0,0 +1,70 @@
require "./spec_helper"
describe Plugin do
describe "helper functions" do
it "mango.text" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.text('<a href="https://github.com">Click Me<a>');
JS
res.should eq "Click Me"
end
end
it "mango.text returns empty string when no text" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.text('<img src="https://github.com" />');
JS
res.should eq ""
end
end
it "mango.css" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.css('<ul><li class="test">A</li><li class="test">B</li><li>C</li></ul>', 'li.test');
JS
res.should eq ["<li class=\"test\">A</li>", "<li class=\"test\">B</li>"]
end
end
it "mango.css returns empty array when no match" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.css('<ul><li class="test">A</li><li class="test">B</li><li>C</li></ul>', 'li.noclass');
JS
res.should eq [] of String
end
end
it "mango.attribute" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.attribute('<a href="https://github.com">Click Me<a>', 'href');
JS
res.should eq "https://github.com"
end
end
it "mango.attribute returns undefined when no match" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.attribute('<div />', 'href') === undefined;
JS
res.should be_true
end
end
# https://github.com/hkalexling/Mango/issues/320
it "mango.attribute handles tags in attribute values" do
with_plugin do |plugin|
res = plugin.eval <<-JS
mango.attribute('<div data-a="<img />" data-b="test" />', 'data-b');
JS
res.should eq "test"
end
end
end
end
+8
View File
@@ -3,6 +3,7 @@ require "../src/queue"
require "../src/server" require "../src/server"
require "../src/config" require "../src/config"
require "../src/main_fiber" require "../src/main_fiber"
require "../src/plugin/plugin"
class State class State
@@hash = {} of String => String @@hash = {} of String => String
@@ -54,3 +55,10 @@ def with_storage
end end
end end
end end
def with_plugin
with_default_config do
plugin = Plugin.new "test", "spec/asset/plugins"
yield plugin
end
end
+43 -23
View File
@@ -1,31 +1,51 @@
require "yaml" require "yaml"
class Config class Config
private OPTIONS = {
"host" => "0.0.0.0",
"port" => 9000,
"base_url" => "/",
"session_secret" => "mango-session-secret",
"library_path" => "~/mango/library",
"library_cache_path" => "~/mango/library.yml.gz",
"db_path" => "~/mango.db",
"queue_db_path" => "~/mango/queue.db",
"scan_interval_minutes" => 5,
"thumbnail_generation_interval_hours" => 24,
"log_level" => "info",
"upload_path" => "~/mango/uploads",
"plugin_path" => "~/mango/plugins",
"download_timeout_seconds" => 30,
"cache_enabled" => true,
"cache_size_mbs" => 50,
"cache_log_enabled" => true,
"disable_login" => false,
"default_username" => "",
"auth_proxy_header_name" => "",
"plugin_update_interval_hours" => 24,
}
include YAML::Serializable include YAML::Serializable
@[YAML::Field(ignore: true)] @[YAML::Field(ignore: true)]
property path = "" property path : String = ""
property host = "0.0.0.0"
property port : Int32 = 9000 # Go through the options constant above and define them as properties.
property base_url = "/" # Allow setting the default values through environment variables.
property session_secret = "mango-session-secret" # Overall precedence: config file > environment variable > default value
property library_path = "~/mango/library" {% begin %}
property library_cache_path = "~/mango/library.yml.gz" {% for k, v in OPTIONS %}
property db_path = "~/mango/mango.db" {% if v.is_a? StringLiteral %}
property queue_db_path = "~/mango/queue.db" property {{k.id}} : String = ENV[{{k.upcase}}]? || {{ v }}
property scan_interval_minutes : Int32 = 5 {% elsif v.is_a? NumberLiteral %}
property thumbnail_generation_interval_hours : Int32 = 24 property {{k.id}} : Int32 = (ENV[{{k.upcase}}]? || {{ v.id }}).to_i
property log_level = "info" {% elsif v.is_a? BoolLiteral %}
property upload_path = "~/mango/uploads" property {{k.id}} : Bool = env_is_true? {{ k.upcase }}, {{ v.id }}
property plugin_path = "~/mango/plugins" {% else %}
property download_timeout_seconds : Int32 = 30 raise "Unknown type in config option: {{ v.class_name.id }}"
property cache_enabled = true {% end %}
property cache_size_mbs = 50 {% end %}
property cache_log_enabled = true {% end %}
property disable_login = false
property default_username = ""
property auth_proxy_header_name = ""
property plugin_update_interval_hours : Int32 = 24
@@singlet : Config? @@singlet : Config?
@@ -38,7 +58,7 @@ class Config
end end
def self.load(path : String?) def self.load(path : String?)
path = "~/.config/mango/config.yml" if path.nil? path = (ENV["CONFIG_PATH"]? || "~/.config/mango/config.yml") if path.nil?
cfg_path = File.expand_path path, home: true cfg_path = File.expand_path path, home: true
if File.exists? cfg_path if File.exists? cfg_path
config = self.from_yaml File.read cfg_path config = self.from_yaml File.read cfg_path
+1
View File
@@ -38,6 +38,7 @@ class Logger
Log.setup do |c| Log.setup do |c|
c.bind "*", @@severity, @backend c.bind "*", @@severity, @backend
c.bind "db.*", :error, @backend c.bind "db.*", :error, @backend
c.bind "duktape", :none, @backend
end end
end end
+1 -1
View File
@@ -7,7 +7,7 @@ require "option_parser"
require "clim" require "clim"
require "tallboy" require "tallboy"
MANGO_VERSION = "0.26.2" MANGO_VERSION = "0.27.0"
# From http://www.network-science.de/ascii/ # From http://www.network-science.de/ascii/
BANNER = %{ BANNER = %{
+30 -9
View File
@@ -105,9 +105,10 @@ class Plugin
getter js_path = "" getter js_path = ""
getter storage_path = "" getter storage_path = ""
def self.build_info_ary def self.build_info_ary(dir : String? = nil)
@@info_ary.clear @@info_ary.clear
dir = Config.current.plugin_path dir ||= Config.current.plugin_path
Dir.mkdir_p dir unless Dir.exists? dir Dir.mkdir_p dir unless Dir.exists? dir
Dir.each_child dir do |f| Dir.each_child dir do |f|
@@ -160,8 +161,8 @@ class Plugin
list.save list.save
end end
def initialize(id : String) def initialize(id : String, dir : String? = nil)
Plugin.build_info_ary Plugin.build_info_ary dir
@info = @@info_ary.find &.id.== id @info = @@info_ary.find &.id.== id
if @info.nil? if @info.nil?
@@ -223,6 +224,10 @@ class Plugin
raise Error.new "Missing required fields in the Page type" raise Error.new "Missing required fields in the Page type"
end end
def can_subscribe? : Bool
info.version > 1 && eval_exists?("newChapters")
end
def search_manga(query : String) def search_manga(query : String)
if info.version == 1 if info.version == 1
raise Error.new "Manga searching is only available for plugins " \ raise Error.new "Manga searching is only available for plugins " \
@@ -315,7 +320,7 @@ class Plugin
json json
end end
private def eval(str) def eval(str)
@rt.eval str @rt.eval str
rescue e : Duktape::SyntaxError rescue e : Duktape::SyntaxError
raise SyntaxError.new e.message raise SyntaxError.new e.message
@@ -327,6 +332,15 @@ class Plugin
JSON.parse eval(str).as String JSON.parse eval(str).as String
end end
private def eval_exists?(str) : Bool
@rt.eval str
true
rescue e : Duktape::ReferenceError
false
rescue e : Duktape::Error
raise Error.new e.message
end
private def def_helper_functions(sbx) private def def_helper_functions(sbx)
sbx.push_object sbx.push_object
@@ -435,9 +449,15 @@ class Plugin
env = Duktape::Sandbox.new ptr env = Duktape::Sandbox.new ptr
html = env.require_string 0 html = env.require_string 0
str = XML.parse(html).inner_text begin
parser = Myhtml::Parser.new html
str = parser.body!.children.first.inner_text
env.push_string str
rescue
env.push_string ""
end
env.push_string str
env.call_success env.call_success
end end
sbx.put_prop_string -2, "text" sbx.put_prop_string -2, "text"
@@ -448,8 +468,9 @@ class Plugin
name = env.require_string 1 name = env.require_string 1
begin begin
attr = XML.parse(html).first_element_child.not_nil![name] parser = Myhtml::Parser.new html
env.push_string attr attr = parser.body!.children.first.attribute_by name
env.push_string attr.not_nil!
rescue rescue
env.push_undefined env.push_undefined
end end
+4 -2
View File
@@ -871,13 +871,15 @@ struct APIRouter
"version" => Int32, "version" => Int32,
"settings" => {} of String => String, "settings" => {} of String => String,
}, },
"subscribable" => Bool,
} }
get "/api/admin/plugin/info" do |env| get "/api/admin/plugin/info" do |env|
begin begin
plugin = Plugin.new env.params.query["plugin"].as String plugin = Plugin.new env.params.query["plugin"].as String
send_json env, { send_json env, {
"success" => true, "success" => true,
"info" => plugin.info, "info" => plugin.info,
"subscribable" => plugin.can_subscribe?,
}.to_json }.to_json
rescue e rescue e
Logger.error e Logger.error e
+3 -2
View File
@@ -64,11 +64,12 @@ class Dir
path = File.join dirname, fn path = File.join dirname, fn
if File.directory? path if File.directory? path
signatures << Dir.contents_signature path, cache signatures << Dir.contents_signature path, cache
signatures << fn if DirEntry.is_valid? path
else else
# Only add its signature value to `signatures` when it is a # Only add its signature value to `signatures` when it is a
# supported file # supported file
signatures << fn if ArchiveEntry.is_valid? fn if ArchiveEntry.is_valid?(fn) || is_supported_image_file(fn)
signatures << fn
end
end end
Fiber.yield Fiber.yield
end end
+6 -5
View File
@@ -1,9 +1,10 @@
IMGS_PER_PAGE = 5 IMGS_PER_PAGE = 5
ENTRIES_IN_HOME_SECTIONS = 8 ENTRIES_IN_HOME_SECTIONS = 8
UPLOAD_URL_PREFIX = "/uploads" UPLOAD_URL_PREFIX = "/uploads"
STATIC_DIRS = %w(/css /js /img /webfonts /favicon.ico /robots.txt) STATIC_DIRS = %w(/css /js /img /webfonts /favicon.ico /robots.txt
SUPPORTED_FILE_EXTNAMES = [".zip", ".cbz", ".rar", ".cbr"] /manifest.json)
SUPPORTED_IMG_TYPES = %w( SUPPORTED_FILE_EXTNAMES = [".zip", ".cbz", ".rar", ".cbr"]
SUPPORTED_IMG_TYPES = %w(
image/jpeg image/jpeg
image/png image/png
image/webp image/webp
@@ -95,9 +96,9 @@ class String
end end
end end
def env_is_true?(key : String) : Bool def env_is_true?(key : String, default : Bool = false) : Bool
val = ENV[key.upcase]? || ENV[key.downcase]? val = ENV[key.upcase]? || ENV[key.downcase]?
return false unless val return default unless val
val.downcase.in? "1", "true" val.downcase.in? "1", "true"
end end
+4 -2
View File
@@ -133,8 +133,10 @@
</template> </template>
<button class="uk-button uk-button-primary" @click.prevent="applyFilters()">Apply</button> <button class="uk-button uk-button-primary" @click.prevent="applyFilters()">Apply</button>
<button class="uk-button uk-button-default" @click.prevent="clearFilters()">Clear</button> <button class="uk-button uk-button-default" @click.prevent="clearFilters()">Clear</button>
<span class="uk-divider-vertical uk-margin-left uk-margin-right"></span> <span x-show="subscribable">
<button class="uk-button uk-button-default" @click.prevent="UIkit.modal($refs.modal).show()" :disable="subscribing">Subscribe</button> <span class="uk-divider-vertical uk-margin-left uk-margin-right"></span>
<button class="uk-button uk-button-default" @click.prevent="UIkit.modal($refs.modal).show()" :disable="subscribing">Subscribe</button>
</span>
</form> </form>
<p class="uk-text-meta" x-show="chapters && chapters.length > chaptersLimit" x-text="`The manga has ${chapters ? chapters.length : 0} chapters, but Mango can only list up to ${chaptersLimit}. Please use the filters to narrow down your search.`"></p> <p class="uk-text-meta" x-show="chapters && chapters.length > chaptersLimit" x-text="`The manga has ${chapters ? chapters.length : 0} chapters, but Mango can only list up to ${chaptersLimit}. Please use the filters to narrow down your search.`"></p>
+18 -7
View File
@@ -5,7 +5,7 @@
<%= render_component "head" %> <%= render_component "head" %>
<body style="position:relative;" x-data="readerComponent()" x-init="init($nextTick)" @resize.window="resized()"> <body style="position:relative;" x-data="readerComponent()" x-init="init($nextTick)" @resize.window="resized()">
<div class="uk-section uk-section-default uk-section-small reader-bg" :style="mode === 'continuous' ? '' : 'padding:0'"> <div class="uk-section uk-section-default uk-section-small reader-bg" :style="mode === 'continuous' ? '' : 'padding:0; position: relative;'">
<div @keydown.window.debounce="keyHandler($event)"></div> <div @keydown.window.debounce="keyHandler($event)"></div>
@@ -19,7 +19,7 @@
</div> </div>
<div <div
:class="{'uk-container': true, 'uk-container-small': mode === 'continuous', 'uk-container-expand': mode !== 'continuous'}"> :class="{'uk-container': true, 'uk-container-small': mode === 'continuous', 'uk-container-expand': mode !== 'continuous'}" style="width: fit-content;">
<div x-show="!loading && mode === 'continuous'" x-cloak> <div x-show="!loading && mode === 'continuous'" x-cloak>
<template x-if="!loading && mode === 'continuous'" x-for="item in items"> <template x-if="!loading && mode === 'continuous'" x-for="item in items">
<img <img
@@ -40,18 +40,18 @@
<%- end -%> <%- end -%>
</div> </div>
<div x-cloak x-show="!loading && mode !== 'continuous'" class="uk-flex uk-flex-middle" style="height:100vh"> <div x-cloak x-show="!loading && mode !== 'continuous'" class="uk-flex uk-flex-middle" :style="`height:${fitType === 'vert' ? '100vh' : ''}; min-width: fit-content;`">
<img uk-img :class="{ <img uk-img :class="{
'uk-align-center': true, 'uk-align-center': true,
'uk-animation-slide-left': flipAnimation === 'left', 'uk-animation-slide-left': flipAnimation === 'left',
'uk-animation-slide-right': flipAnimation === 'right' 'uk-animation-slide-right': flipAnimation === 'right'
}" :data-src="curItem.url" :width="curItem.width" :height="curItem.height" :id="curItem.id" @click="clickImage($event)" :style="` }" :data-src="curItem.url" :width="curItem.width" :height="curItem.height" :id="curItem.id" @click="clickImage($event)" :style="`
width:${mode === 'width' ? '100vw' : 'auto'}; width:${fitType === 'horz' ? '100vw' : 'auto'};
height:${mode === 'height' ? '100vh' : 'auto'}; height:${fitType === 'vert' ? '100vh' : 'auto'};
margin-bottom:0; margin-bottom:0;
max-width:100%; max-width:${fitType === 'horz' ? '100%' : fitType === 'vert' ? '' : 'none' };
max-height:100%; max-height:${fitType === 'vert' ? '100%' : fitType === 'horz' ? '' : 'none'};
object-fit: contain; object-fit: contain;
`" /> `" />
@@ -94,6 +94,17 @@
</div> </div>
</div> </div>
<div class="uk-margin" x-show="mode !== 'continuous'">
<label class="uk-form-label" for="mode-select">Page fit</label>
<div class="uk-form-controls">
<select id="fit-select" class="uk-select" @change="fitChanged()">
<option value="vert">Fit height</option>
<option value="horz">Fit width</option>
<option value="real">Real size</option>
</select>
</div>
</div>
<div class="uk-margin" x-show="mode === 'continuous'"> <div class="uk-margin" x-show="mode === 'continuous'">
<label class="uk-form-label" for="margin-range" x-text="`Page Margin: ${margin}px`"></label> <label class="uk-form-label" for="margin-range" x-text="`Page Margin: ${margin}px`"></label>
<div class="uk-form-controls"> <div class="uk-form-controls">