Begin rust rewrite

This commit is contained in:
Rémi BERTHO 2022-09-05 14:16:10 +02:00
parent 0dc8c22491
commit dfcbd9cecf
Signed by: dalan
GPG key ID: EE3B917931C07B64
22 changed files with 23822 additions and 1096 deletions

15
.gitignore vendored
View file

@ -102,3 +102,18 @@ ENV/
# Others
*.db
*.db:encryptable
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# IDE
.idea

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "ext/htmlfilemerger"]
path = ext/htmlfilemerger
url = https://github.com/DarkTrick/htmlfilemerger.git

1996
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

13
Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "simple_panorama_viewer"
version = "2.0.0"
edition = "2021"
[dependencies]
anyhow = "1.0"
log = "0.4"
simplelog = "0.12"
wry = "0.20"
rfd = "0.10"
rust-embed = "6"
base64 = "0.13"

View file

@ -1,7 +1,7 @@
The MIT License (MIT)
=====================
Copyright © 2018 Rémi BERTHO
Copyright © 2018-2022 Rémi BERTHO
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View file

@ -1,49 +0,0 @@
#!/usr/bin/env python3
import os
import sys
from os import path
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngine import *
from PyQt5.QtWebEngineWidgets import *
app = QApplication(sys.argv)
app.setApplicationName("SimplePanoramaViewer")
if len(sys.argv) < 2:
img_dialog = QFileDialog.getOpenFileName(None, "Open image", str(Path.home()), "JPG Images (*.jpg *.JPG *.jpeg *.pjpeg *.pjpg *.PJPG)")
img = img_dialog[0]
else:
img = path.abspath(sys.argv[1])
if img == "":
exit(0)
img = img.replace("\\", "/")
img = img.replace("'", "\\'")
if getattr( sys, 'frozen', False ) :
exe_dir = path.realpath(sys._MEIPASS)
else :
exe_dir = path.dirname(path.realpath(__file__))
os.chdir(exe_dir)
try:
with open("html/index.html", "r", encoding="utf8") as file:
html = file.read()
except OSError as exception:
print("Error: HTML file not found: " + str(exception))
html = html.replace("__IMG_PATH__", img)
web = QWebEngineView()
url = QUrl.fromLocalFile(exe_dir + "/index.html")
web.setHtml(html,url)
web.setWindowTitle(img)
web.showMaximized()
web.show()
sys.exit(app.exec_())

View file

@ -1,39 +0,0 @@
# -*- mode: python -*-
block_cipher = None
added_files = [
( 'README.md', '.' ),
( 'LICENSE.md', '.' ),
( 'css', 'css' ),
( 'html', 'html' ),
( 'js', 'js' )
]
a = Analysis(['SimplePanoramaViewer'],
pathex=['./SimplePanoramaViewer'],
binaries=[],
datas=added_files,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='SimplePanoramaViewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False )

View file

@ -1,43 +0,0 @@
# -*- mode: python -*-
block_cipher = None
added_files = [
( 'README.md', '.' ),
( 'LICENSE.md', '.' ),
( 'css', 'css' ),
( 'html', 'html' ),
( 'js', 'js' )
]
a = Analysis(['SimplePanoramaViewer'],
pathex=['./SimplePanoramaViewer'],
binaries=[],
datas=added_files,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='SimplePanoramaViewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='SimplePanoramaViewer')

654
css/photo-sphere-viewer.css Normal file
View file

@ -0,0 +1,654 @@
/*!
* Photo Sphere Viewer 4.7.1
* @copyright 2014-2015 Jérémy Heleine
* @copyright 2015-2022 Damien "Mistic" Sorel
* @licence MIT (https://opensource.org/licenses/MIT)
*/
.psv-container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
position: relative;
background: radial-gradient(#fff 0%, #fdfdfd 16%, #fbfbfb 33%, #f8f8f8 49%, #efefef 66%, #dfdfdf 82%, #bfbfbf 100%);
overflow: hidden;
}
.psv-container--fullscreen {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.psv-canvas-container {
position: absolute;
top: 0;
left: 0;
z-index: 0;
-webkit-transition: opacity linear 100ms;
transition: opacity linear 100ms;
}
.psv-canvas {
display: block;
}
.psv-loader-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 80;
}
.psv-loader {
position: relative;
text-align: center;
color: rgba(61, 61, 61, 0.7);
width: 150px;
height: 150px;
border: 10px solid transparent;
}
.psv-loader::before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.psv-loader, .psv-loader-image, .psv-loader-text {
display: inline-block;
vertical-align: middle;
}
.psv-loader-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.psv-loader-text {
font: 14px sans-serif;
}
.psv-navbar {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: absolute;
z-index: 90;
bottom: -40px;
left: 0;
width: 100%;
height: 40px;
background: rgba(61, 61, 61, 0.5);
-webkit-transition: bottom ease-in-out 0.1s;
transition: bottom ease-in-out 0.1s;
}
.psv-navbar--open {
bottom: 0;
}
.psv-navbar, .psv-navbar * {
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.psv-caption {
-webkit-box-flex: 1;
-ms-flex: 1 1 100%;
flex: 1 1 100%;
color: rgba(255, 255, 255, 0.7);
overflow: hidden;
text-align: center;
}
.psv-caption-icon {
height: 20px;
width: 20px;
cursor: pointer;
}
.psv-caption-icon * {
fill: rgba(255, 255, 255, 0.7);
}
.psv-caption-content {
display: inline-block;
padding: 10px;
font: 16px sans-serif;
white-space: nowrap;
}
.psv-button {
-webkit-box-flex: 0;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding: 10px;
position: relative;
cursor: pointer;
height: 20px;
width: 20px;
background: transparent;
color: rgba(255, 255, 255, 0.7);
}
.psv-button--active {
background: rgba(255, 255, 255, 0.2);
}
.psv-button--disabled {
pointer-events: none;
opacity: 0.5;
}
.psv-button-svg {
width: 100%;
-webkit-transform: scale(1);
transform: scale(1);
-webkit-transition: -webkit-transform 200ms ease;
transition: -webkit-transform 200ms ease;
transition: transform 200ms ease;
transition: transform 200ms ease, -webkit-transform 200ms ease;
}
.psv-button:not(.psv-button--disabled):focus-visible {
outline: 2px dotted currentcolor;
outline-offset: -2px;
}
.psv-container:not(.psv--is-touch) .psv-button--hover-scale:not(.psv-button--disabled):hover .psv-button-svg {
-webkit-transform: scale(1.2);
transform: scale(1.2);
}
.psv-move-button + .psv-move-button {
margin-left: -10px;
}
.psv-custom-button {
width: auto;
}
.psv-autorotate-button.psv-button {
width: 25px;
height: 25px;
padding: 7.5px;
}
.psv-zoom-range.psv-button {
width: 80px;
height: 1px;
margin: 10px 0;
padding: 9.5px 0;
max-width: 600px;
}
.psv-zoom-range-line {
position: relative;
width: 80px;
height: 1px;
background: rgba(255, 255, 255, 0.7);
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
}
.psv-zoom-range-handle {
position: absolute;
border-radius: 50%;
top: -3px;
width: 7px;
height: 7px;
background: rgba(255, 255, 255, 0.7);
-webkit-transform: scale(1);
transform: scale(1);
-webkit-transition: -webkit-transform 0.3s ease;
transition: -webkit-transform 0.3s ease;
transition: transform 0.3s ease;
transition: transform 0.3s ease, -webkit-transform 0.3s ease;
}
.psv-zoom-range:not(.psv-button--disabled):hover .psv-zoom-range-line {
-webkit-box-shadow: 0 0 2px rgba(255, 255, 255, 0.7);
box-shadow: 0 0 2px rgba(255, 255, 255, 0.7);
}
.psv-zoom-range:not(.psv-button--disabled):hover .psv-zoom-range-handle {
-webkit-transform: scale(1.3);
transform: scale(1.3);
}
.psv-notification {
position: absolute;
z-index: 100;
bottom: -40px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
padding: 0 2em;
opacity: 0;
-webkit-transition-property: opacity, bottom;
transition-property: opacity, bottom;
-webkit-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
-webkit-transition-duration: 200ms;
transition-duration: 200ms;
}
.psv-notification-content {
max-width: 50em;
background-color: rgba(61, 61, 61, 0.8);
border-radius: 4px;
padding: 0.5em 1em;
font: 14px sans-serif;
color: rgb(255, 255, 255);
}
.psv-notification--visible {
opacity: 100;
bottom: 80px;
}
.psv-overlay {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
z-index: 110;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: radial-gradient(#fff 0%, #fdfdfd 16%, #fbfbfb 33%, #f8f8f8 49%, #efefef 66%, #dfdfdf 82%, #bfbfbf 100%);
color: black;
opacity: 0.8;
}
.psv-overlay-image {
margin-bottom: 4vh;
}
.psv-overlay-image svg {
width: 50vw;
}
@media (orientation: landscape) {
.psv-overlay-image svg {
width: 25vw;
}
}
.psv-overlay-text {
font: 30px sans-serif;
text-align: center;
}
.psv-overlay-subtext {
font: 20px sans-serif;
opacity: 0.8;
text-align: center;
}
.psv-panel {
position: absolute;
z-index: 90;
right: 0;
height: 100%;
width: 400px;
max-width: calc(100% - 24px);
background: rgba(10, 10, 10, 0.7);
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
opacity: 0;
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
-webkit-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
-webkit-transition-duration: 0.1s;
transition-duration: 0.1s;
cursor: default;
margin-left: 9px;
}
.psv--has-navbar .psv-panel {
height: calc(100% - 40px);
}
.psv-panel-close-button {
display: none;
position: absolute;
top: 0;
left: -24px;
width: 24px;
height: 24px;
background: rgba(0, 0, 0, 0.9);
}
.psv-panel-close-button::before, .psv-panel-close-button::after {
content: "";
position: absolute;
top: 50%;
left: 4px;
width: 15px;
height: 1px;
background-color: #fff;
-webkit-transition: 0.2s ease-in-out;
transition: 0.2s ease-in-out;
-webkit-transition-property: width, left, -webkit-transform;
transition-property: width, left, -webkit-transform;
transition-property: width, left, transform;
transition-property: width, left, transform, -webkit-transform;
}
.psv-panel-close-button::before {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.psv-panel-close-button::after {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.psv-panel-close-button:hover::before, .psv-panel-close-button:hover::after {
left: 0;
width: 23px;
}
.psv-panel-close-button:hover::before {
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
}
.psv-panel-close-button:hover::after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.psv-panel-resizer {
display: none;
position: absolute;
top: 0;
left: -9px;
width: 9px;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
cursor: col-resize;
}
.psv-panel-resizer::before {
content: "";
position: absolute;
top: 50%;
left: 1px;
margin-top: -14.5px;
width: 1px;
height: 1px;
-webkit-box-shadow: 1px 0 #fff, 3px 0px #fff, 5px 0px #fff, 1px 2px #fff, 3px 2px #fff, 5px 2px #fff, 1px 4px #fff, 3px 4px #fff, 5px 4px #fff, 1px 6px #fff, 3px 6px #fff, 5px 6px #fff, 1px 8px #fff, 3px 8px #fff, 5px 8px #fff, 1px 10px #fff, 3px 10px #fff, 5px 10px #fff, 1px 12px #fff, 3px 12px #fff, 5px 12px #fff, 1px 14px #fff, 3px 14px #fff, 5px 14px #fff, 1px 16px #fff, 3px 16px #fff, 5px 16px #fff, 1px 18px #fff, 3px 18px #fff, 5px 18px #fff, 1px 20px #fff, 3px 20px #fff, 5px 20px #fff, 1px 22px #fff, 3px 22px #fff, 5px 22px #fff, 1px 24px #fff, 3px 24px #fff, 5px 24px #fff, 1px 26px #fff, 3px 26px #fff, 5px 26px #fff, 1px 28px #fff, 3px 28px #fff, 5px 28px #fff;
box-shadow: 1px 0 #fff, 3px 0px #fff, 5px 0px #fff, 1px 2px #fff, 3px 2px #fff, 5px 2px #fff, 1px 4px #fff, 3px 4px #fff, 5px 4px #fff, 1px 6px #fff, 3px 6px #fff, 5px 6px #fff, 1px 8px #fff, 3px 8px #fff, 5px 8px #fff, 1px 10px #fff, 3px 10px #fff, 5px 10px #fff, 1px 12px #fff, 3px 12px #fff, 5px 12px #fff, 1px 14px #fff, 3px 14px #fff, 5px 14px #fff, 1px 16px #fff, 3px 16px #fff, 5px 16px #fff, 1px 18px #fff, 3px 18px #fff, 5px 18px #fff, 1px 20px #fff, 3px 20px #fff, 5px 20px #fff, 1px 22px #fff, 3px 22px #fff, 5px 22px #fff, 1px 24px #fff, 3px 24px #fff, 5px 24px #fff, 1px 26px #fff, 3px 26px #fff, 5px 26px #fff, 1px 28px #fff, 3px 28px #fff, 5px 28px #fff;
background: transparent;
}
.psv-panel-content {
width: 100%;
height: 100%;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: rgb(220, 220, 220);
font: 16px sans-serif;
overflow: auto;
}
.psv-panel-content:not(.psv-panel-content--no-margin) {
padding: 1em;
}
.psv-panel-content--no-interaction {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
}
.psv-panel--open {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
opacity: 1;
-webkit-transition-duration: 0.2s;
transition-duration: 0.2s;
}
.psv-panel--open .psv-panel-close-button,
.psv-panel--open .psv-panel-resizer {
display: block;
}
.psv-panel-menu {
height: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
.psv-panel-menu-title {
-webkit-box-flex: 0;
-ms-flex: none;
flex: none;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font: 24px sans-serif;
margin: 24px 12px;
}
.psv-panel-menu-title svg {
width: 24px;
height: 24px;
margin-right: 12px;
}
.psv-panel-menu-list {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
list-style: none;
margin: 0;
padding: 0;
overflow-x: hidden;
}
.psv-panel-menu-item {
min-height: 20px;
padding: 0.5em 1em;
cursor: pointer;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-transition: background 0.1s ease-in-out;
transition: background 0.1s ease-in-out;
}
.psv-panel-menu-item--active {
outline: 1px solid currentcolor;
outline-offset: -1px;
}
.psv-panel-menu-item-icon {
-webkit-box-flex: 0;
-ms-flex: none;
flex: none;
height: 20px;
width: 20px;
margin-right: 0.5em;
}
.psv-panel-menu-item-icon img {
max-width: 100%;
max-height: 100%;
}
.psv-panel-menu-item-icon svg {
width: 100%;
height: 100%;
}
.psv-panel-menu-item:focus-visible {
outline: 1px dotted currentcolor;
outline-offset: -1px;
}
.psv-panel-menu--stripped .psv-panel-menu-item:hover {
background: rgba(255, 255, 255, 0.2);
}
.psv-panel-menu--stripped .psv-panel-menu-item:nth-child(odd), .psv-panel-menu--stripped .psv-panel-menu-item:nth-child(odd)::before {
background: rgba(255, 255, 255, 0.1);
}
.psv-panel-menu--stripped .psv-panel-menu-item:nth-child(even), .psv-panel-menu--stripped .psv-panel-menu-item:nth-child(even)::before {
background: transparent;
}
.psv-container:not(.psv--is-touch) .psv-panel-menu-item:hover {
background: rgba(255, 255, 255, 0.2);
}
.psv-tooltip {
position: absolute;
z-index: 50;
-webkit-box-sizing: border-box;
box-sizing: border-box;
max-width: 200px;
background-color: rgba(61, 61, 61, 0.8);
border-radius: 4px;
padding: 0.5em 1em;
opacity: 0;
-webkit-transition-property: opacity;
transition-property: opacity;
-webkit-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
-webkit-transition-duration: 100ms;
transition-duration: 100ms;
outline: 5px solid transparent;
}
.psv-tooltip-content {
color: rgb(255, 255, 255);
font: 14px sans-serif;
text-shadow: 0 1px #000;
}
.psv-tooltip-arrow {
position: absolute;
height: 0;
width: 0;
border: 7px solid transparent;
}
.psv-tooltip--bottom-center {
-webkit-box-shadow: 0 3px 0 rgba(90, 90, 90, 0.7);
box-shadow: 0 3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, -5px, 0);
transform: translate3d(0, -5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--bottom-center .psv-tooltip-arrow {
border-bottom-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--center-left {
-webkit-box-shadow: -3px 0 0 rgba(90, 90, 90, 0.7);
box-shadow: -3px 0 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(5px, 0, 0);
transform: translate3d(5px, 0, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--center-left .psv-tooltip-arrow {
border-left-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--top-center {
-webkit-box-shadow: 0 -3px 0 rgba(90, 90, 90, 0.7);
box-shadow: 0 -3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, 5px, 0);
transform: translate3d(0, 5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--top-center .psv-tooltip-arrow {
border-top-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--center-right {
-webkit-box-shadow: 3px 0 0 rgba(90, 90, 90, 0.7);
box-shadow: 3px 0 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(-5px, 0, 0);
transform: translate3d(-5px, 0, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--center-right .psv-tooltip-arrow {
border-right-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--bottom-left {
-webkit-box-shadow: -3px 3px 0 rgba(90, 90, 90, 0.7);
box-shadow: -3px 3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, -5px, 0);
transform: translate3d(0, -5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--bottom-left .psv-tooltip-arrow {
border-bottom-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--bottom-right {
-webkit-box-shadow: 3px 3px 0 rgba(90, 90, 90, 0.7);
box-shadow: 3px 3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, -5px, 0);
transform: translate3d(0, -5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--bottom-right .psv-tooltip-arrow {
border-bottom-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--top-left {
-webkit-box-shadow: -3px -3px 0 rgba(90, 90, 90, 0.7);
box-shadow: -3px -3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, 5px, 0);
transform: translate3d(0, 5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--top-left .psv-tooltip-arrow {
border-top-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--top-right {
-webkit-box-shadow: 3px -3px 0 rgba(90, 90, 90, 0.7);
box-shadow: 3px -3px 0 rgba(90, 90, 90, 0.7);
-webkit-transform: translate3d(0, 5px, 0);
transform: translate3d(0, 5px, 0);
-webkit-transition-property: opacity, -webkit-transform;
transition-property: opacity, -webkit-transform;
transition-property: opacity, transform;
transition-property: opacity, transform, -webkit-transform;
}
.psv-tooltip--top-right .psv-tooltip-arrow {
border-top-color: rgba(61, 61, 61, 0.8);
}
.psv-tooltip--visible {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
opacity: 1;
-webkit-transition-duration: 100ms;
transition-duration: 100ms;
}
/*# sourceMappingURL=photo-sphere-viewer.css.map */

File diff suppressed because one or more lines are too long

10891
embed/index.html Normal file

File diff suppressed because one or more lines are too long

1
ext/htmlfilemerger Submodule

@ -0,0 +1 @@
Subproject commit cf3a34373ff48519d699e08f8a0abf7c83e432c2

View file

@ -5,7 +5,7 @@
<title>Panorama viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/photo-sphere-viewer.min.css">
<link rel="stylesheet" href="../css/photo-sphere-viewer.css">
<style>
html, body {
@ -38,22 +38,20 @@
<div id="photosphere"></div>
<script src="js/three.min.js"></script>
<script src="js/D.min.js"></script>
<script src="js/uevent.min.js"></script>
<script src="js/doT.min.js"></script>
<script src="js/photo-sphere-viewer.min.js"></script>
<script src="../js/three.min.js"></script>
<script src="../js/browser.js"></script>
<script src="../js/photo-sphere-viewer.js"></script>
<h1>Panorama</h1>>
<script>
var PSV = new PhotoSphereViewer({
var PSV = new PhotoSphereViewer.Viewer({
container: 'photosphere',
panorama: '__IMG_PATH__',
caption: 'Panorama displayed with Photo Sphere Viewer V3.5.1',
time_anim: false,
default_fov: 40,
min_fov: 5,
max_fov: 75,
caption: 'Panorama displayed with Photo Sphere Viewer V4.7.1',
autorotateDelay: false,
defaultZoomLvl: 40,
minFov: 5,
maxFov: 75,
navbar: [
'autorotate',
'zoom',

2
js/D.min.js vendored
View file

@ -1,2 +0,0 @@
/* (c) Jonathan Gotti - licence: https://github.com/malko/d.js/LICENCE.txt @version 0.7.5*/
!function(a){"use strict";function b(a){l(function(){throw a})}function c(b){return this.then(b,a)}function d(b){return this.then(a,b)}function e(b,c){return this.then(function(a){return m(b)?b.apply(null,n(a)?a:[a]):v.onlyFuncs?a:b},c||a)}function f(a){function b(){a()}return this.then(b,b),this}function g(a){return this.then(function(b){return m(a)?a.apply(null,n(b)?b.splice(0,0,void 0)&&b:[void 0,b]):v.onlyFuncs?b:a},function(b){return a(b)})}function h(c){return this.then(a,c?function(a){throw c(a),a}:b)}function i(a,b){var c=q(a);if(1===c.length&&n(c[0])){if(!c[0].length)return v.fulfilled([]);c=c[0]}var d=[],e=v(),f=c.length;if(f)for(var g=function(a){c[a]=v.promisify(c[a]),c[a].then(function(g){d[a]=b?c[a]:g,--f||e.resolve(d)},function(g){b?(d[a]=c[a],--f||e.resolve(d)):e.reject(g)})},h=0,i=f;i>h;h++)g(h);else e.resolve(d);return e.promise}function j(a,b){return a.then(m(b)?b:function(){return b})}function k(a){var b=q(a);1===b.length&&n(b[0])&&(b=b[0]);for(var c=v(),d=0,e=b.length,f=v.resolved();e>d;d++)f=j(f,b[d]);return c.resolve(f),c.promise}var l,m=function(a){return"function"==typeof a},n=function(a){return Array.isArray?Array.isArray(a):a instanceof Array},o=function(a){return!(!a||!(typeof a).match(/function|object/))},p=function(b){return b===!1||b===a||null===b},q=function(a,b){return[].slice.call(a,b)},r="undefined",s=typeof TypeError===r?Error:TypeError;if(typeof process!==r&&process.nextTick)l=process.nextTick;else if(typeof MessageChannel!==r){var t=new MessageChannel,u=[];t.port1.onmessage=function(){u.length&&u.shift()()},l=function(a){u.push(a),t.port2.postMessage(0)}}else l=function(a){setTimeout(a,0)};var v=function(b){function i(){if(0!==r){var a,b=t,c=0,d=b.length,e=~r?0:1;for(t=[];d>c;c++)(a=b[c][e])&&a(n)}}function j(a){function b(a){return function(b){return c?void 0:(c=!0,a(b))}}var c=!1;if(r)return this;try{var d=o(a)&&a.then;if(m(d)){if(a===u)throw new s("Promise can't resolve itself");return d.call(a,b(j),b(k)),this}}catch(e){return b(k)(e),this}return q(function(){n=a,r=1,i()}),this}function k(a){return r||q(function(){try{throw a}catch(b){n=b}r=-1,i()}),this}var n,q=(a!==b?b:v.alwaysAsync)?l:function(a){a()},r=0,t=[],u={then:function(a,b){var c=v();return t.push([function(b){try{p(a)?c.resolve(b):c.resolve(m(a)?a(b):v.onlyFuncs?b:a)}catch(d){c.reject(d)}},function(a){if((p(b)||!m(b)&&v.onlyFuncs)&&c.reject(a),b)try{c.resolve(m(b)?b(a):b)}catch(d){c.reject(d)}}]),0!==r&&q(i),c.promise},success:c,error:d,otherwise:d,apply:e,spread:e,ensure:f,nodify:g,rethrow:h,isPending:function(){return 0===r},getStatus:function(){return r}};return u.toSource=u.toString=u.valueOf=function(){return n===a?this:n},{promise:u,resolve:j,fulfill:j,reject:k}};if(v.deferred=v.defer=v,v.nextTick=l,v.alwaysAsync=!0,v.onlyFuncs=!0,v.resolve=v.resolved=v.fulfilled=function(a){return v(!0).resolve(a).promise},v.reject=v.rejected=function(a){return v(!0).reject(a).promise},v.wait=function(a){var b=v();return setTimeout(b.resolve,a||0),b.promise},v.delay=function(a,b){var c=v();return setTimeout(function(){try{c.resolve(m(a)?a.apply(null):a)}catch(b){c.reject(b)}},b||0),c.promise},v.promisify=function(a){return a&&m(a.then)?a:v.resolved(a)},v.all=function(){return i(arguments,!1)},v.resolveAll=function(){return i(arguments,!0)},v.sequence=function(){return k(arguments)},v.nodeCapsule=function(a,b){return b||(b=a,a=void 0),function(){var c=v(),d=q(arguments);d.push(function(a,b){a?c.reject(a):c.resolve(arguments.length>2?q(arguments,1):b)});try{b.apply(a,d)}catch(e){c.reject(e)}return c.promise}},"function"==typeof define&&define.amd)define("D.js",[],function(){return v});else if(typeof module!==r&&module.exports)module.exports=v;else if(typeof window!==r){var w=window.D;v.noConflict=function(){return window.D=w,v},window.D=v}}();

333
js/browser.js Normal file
View file

@ -0,0 +1,333 @@
/*!
* uevent (v2.2.0)
* @copyright 2015-2022 Damien "Mistic" Sorel <contact@git.strangeplanet.fr>
* @licence MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.uEvent = {}));
})(this, (function (exports) { 'use strict';
var returnTrue = function returnTrue() {
return true;
};
var returnFalse = function returnFalse() {
return false;
};
var Event = /*#__PURE__*/function () {
/**
* @param {*} target
* @param {String} type
* @param {Array} args
*/
function Event(target, type, args) {
Object.defineProperties(this, {
'target': {
get: function get() {
return target;
},
set: function set() {},
enumerable: true
},
'type': {
get: function get() {
return type;
},
set: function set() {},
enumerable: true
},
'args': {
get: function get() {
return args;
},
set: function set() {},
enumerable: true
}
});
this.isDefaultPrevented = returnFalse;
this.isPropagationStopped = returnFalse;
}
var _proto = Event.prototype;
_proto.preventDefault = function preventDefault() {
this.isDefaultPrevented = returnTrue;
};
_proto.stopPropagation = function stopPropagation() {
this.isPropagationStopped = returnTrue;
};
return Event;
}();
var Event_1 = Event;
/**
* @typedef {Object.<String, Function>} Callbacks
*/
var EventEmitter = /*#__PURE__*/function () {
function EventEmitter() {}
var _proto = EventEmitter.prototype;
/**
* Add one or many event handlers
*
* @example
* obj.on('event', callback)
* obj.on('event', listener) // listener has an `handleEvent` method
* obj.on('event1 event2', callback)
* obj.on({ event1: callback1, event2: callback2 })
*
* @param {String|Callbacks} events
* @param {Function} [callback]
* @return {this}
*/
_proto.on = function on(events, callback) {
var _this = this;
this.__events = this.__events || {};
if (typeof events === 'object') {
for (var event in events) {
if (events.hasOwnProperty(event)) {
this.__events[event] = this.__events[event] || [];
this.__events[event].push(events[event]);
}
}
} else {
events.split(' ').forEach(function (event) {
_this.__events[event] = _this.__events[event] || [];
_this.__events[event].push(callback);
});
}
return this;
}
/**
* Remove one or many or all event handlers
*
* @example
* obj.off('event')
* obj.off('event', callback)
* obj.off('event1 event2')
* obj.off({ event1: callback1, event2: callback2 })
* obj.off()
*
* @param {String|Callbacks} [events]
* @param {Function} [callback]
* @return {this}
*/
;
_proto.off = function off(events, callback) {
var _this2 = this;
if (typeof events === 'object') {
for (var event in events) {
if (events.hasOwnProperty(event)) {
if (this.__events && event in this.__events) {
var index = this.__events[event].indexOf(events[event]);
if (index !== -1) this.__events[event].splice(index, 1);
}
if (this.__once && event in this.__once) {
var _index = this.__once[event].indexOf(events[event]);
if (_index !== -1) this.__once[event].splice(_index, 1);
}
}
}
} else if (!!events) {
events.split(' ').forEach(function (event) {
if (_this2.__events && event in _this2.__events) {
if (callback) {
var _index2 = _this2.__events[event].indexOf(callback);
if (_index2 !== -1) _this2.__events[event].splice(_index2, 1);
} else {
_this2.__events[event].length = 0;
}
}
if (_this2.__once && event in _this2.__once) {
if (callback) {
var _index3 = _this2.__once[event].indexOf(callback);
if (_index3 !== -1) _this2.__once[event].splice(_index3, 1);
} else {
_this2.__once[event].length = 0;
}
}
});
} else {
this.__events = {};
this.__once = {};
}
return this;
}
/**
* Add one or many event handlers that will be called only once
* This handlers are only applicable to "trigger", not "change"
*
* @example
* obj.once('event', callback)
* obj.once('event1 event2', callback)
* obj.once({ event1: callback1, event2: callback2 })
*
* @param {String|Callbacks} events
* @param {Function} [callback]
* @return {this}
*/
;
_proto.once = function once(events, callback) {
var _this3 = this;
this.__once = this.__once || {};
if (typeof events === 'object') {
for (var event in events) {
if (events.hasOwnProperty(event)) {
this.__once[event] = this.__once[event] || [];
this.__once[event].push(events[event]);
}
}
} else {
events.split(' ').forEach(function (event) {
_this3.__once[event] = _this3.__once[event] || [];
_this3.__once[event].push(callback);
});
}
return this;
}
/**
* Trigger all handlers for an event
*
* @param {String} event
* @param {*...} [arguments]
* @return {Event}
*/
;
_proto.trigger = function trigger(event
/* , args... */
) {
var args = Array.prototype.slice.call(arguments, 1);
var e = new Event_1(this, event, args);
if (this.__events && event in this.__events) {
for (var i = 0, l = this.__events[event].length; i < l; i++) {
var f = this.__events[event][i];
if (typeof f === 'object') {
f.handleEvent(e);
} else {
f.call.apply(f, [this, e].concat(args));
}
if (e.isPropagationStopped()) {
break;
}
}
}
if (this.__once && event in this.__once) {
for (var _i = 0, _l = this.__once[event].length; _i < _l; _i++) {
var _f = this.__once[event][_i];
if (typeof _f === 'object') {
_f.handleEvent(e);
} else {
_f.call.apply(_f, [this, e].concat(args));
}
if (e.isPropagationStopped()) {
break;
}
}
delete this.__once[event];
}
return e;
}
/**
* Trigger all modificators for an event, each handler must return a value
*
* @param {String} event
* @param {*} value
* @param {*...} [arguments]
* @return {*} modified value
*/
;
_proto.change = function change(event, value
/* , args... */
) {
var args = Array.prototype.slice.call(arguments, 2);
var e = new Event_1(this, event, args);
e.value = value;
if (this.__events && event in this.__events) {
for (var i = 0, l = this.__events[event].length; i < l; i++) {
var f = this.__events[event][i];
if (typeof f === 'object') {
e.value = f.handleEvent(e);
} else {
e.value = f.call.apply(f, [this, e, e.value].concat(args));
}
if (e.isPropagationStopped()) {
break;
}
}
}
return e.value;
};
return EventEmitter;
}();
var EventEmitter_1 = EventEmitter;
function mixin(target) {
target = typeof target === 'function' ? target.prototype : target;
['on', 'off', 'once', 'trigger', 'change'].forEach(function (name) {
target[name] = EventEmitter_1.prototype[name];
});
return target;
}
var uEvent = {
EventEmitter: EventEmitter_1,
Event: Event_1,
mixin: mixin
};
var uEvent_1 = uEvent.EventEmitter;
var uEvent_2 = uEvent.Event;
var uEvent_3 = uEvent.mixin;
exports.Event = uEvent_2;
exports.EventEmitter = uEvent_1;
exports["default"] = uEvent;
exports.mixin = uEvent_3;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=browser.js.map

1
js/browser.js.map Normal file

File diff suppressed because one or more lines are too long

2
js/doT.min.js vendored
View file

@ -1,2 +0,0 @@
/* Laura Doktorova https://github.com/olado/doT */
!function(){"use strict";function e(n,t,r){return("string"==typeof t?t:t.toString()).replace(n.define||a,function(e,t,o,a){return 0===t.indexOf("def.")&&(t=t.substring(4)),t in r||(":"===o?(n.defineParams&&a.replace(n.defineParams,function(e,n,o){r[t]={arg:n,text:o}}),t in r||(r[t]=a)):new Function("def","def['"+t+"']="+a)(r)),""}).replace(n.use||a,function(t,o){n.useParams&&(o=o.replace(n.useParams,function(e,n,t,o){if(r[t]&&r[t].arg&&o){var a=(t+":"+o).replace(/'|\\/g,"_");return r.__exp=r.__exp||{},r.__exp[a]=r[t].text.replace(new RegExp("(^|[^\\w$])"+r[t].arg+"([^\\w$])","g"),"$1"+o+"$2"),n+"def.__exp['"+a+"']"}}));var a=new Function("def","return "+o)(r);return a?e(n,a,r):a})}function n(e){return e.replace(/\\('|\\)/g,"$1").replace(/[\r\t\n]/g," ")}var t,r={name:"doT",version:"1.1.1",templateSettings:{evaluate:/\{\{([\s\S]+?(\}?)+)\}\}/g,interpolate:/\{\{=([\s\S]+?)\}\}/g,encode:/\{\{!([\s\S]+?)\}\}/g,use:/\{\{#([\s\S]+?)\}\}/g,useParams:/(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,define:/\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,defineParams:/^\s*([\w$]+):([\s\S]+)/,conditional:/\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,iterate:/\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,varname:"it",strip:!0,append:!0,selfcontained:!1,doNotSkipEncoded:!1},template:void 0,compile:void 0,log:!0};r.encodeHTMLSource=function(e){var n={"&":"&#38;","<":"&#60;",">":"&#62;",'"':"&#34;","'":"&#39;","/":"&#47;"},t=e?/[&<>"'\/]/g:/&(?!#?\w+;)|<|>|"|'|\//g;return function(e){return e?e.toString().replace(t,function(e){return n[e]||e}):""}},t=function(){return this||(0,eval)("this")}(),"undefined"!=typeof module&&module.exports?module.exports=r:"function"==typeof define&&define.amd?define(function(){return r}):t.doT=r;var o={append:{start:"'+(",end:")+'",startencode:"'+encodeHTML("},split:{start:"';out+=(",end:");out+='",startencode:"';out+=encodeHTML("}},a=/$^/;r.template=function(c,i,u){i=i||r.templateSettings;var d,s,p=i.append?o.append:o.split,l=0,f=i.use||i.define?e(i,c,u||{}):c;f=("var out='"+(i.strip?f.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ").replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""):f).replace(/'|\\/g,"\\$&").replace(i.interpolate||a,function(e,t){return p.start+n(t)+p.end}).replace(i.encode||a,function(e,t){return d=!0,p.startencode+n(t)+p.end}).replace(i.conditional||a,function(e,t,r){return t?r?"';}else if("+n(r)+"){out+='":"';}else{out+='":r?"';if("+n(r)+"){out+='":"';}out+='"}).replace(i.iterate||a,function(e,t,r,o){return t?(l+=1,s=o||"i"+l,t=n(t),"';var arr"+l+"="+t+";if(arr"+l+"){var "+r+","+s+"=-1,l"+l+"=arr"+l+".length-1;while("+s+"<l"+l+"){"+r+"=arr"+l+"["+s+"+=1];out+='"):"';} } out+='"}).replace(i.evaluate||a,function(e,t){return"';"+n(t)+"out+='"})+"';return out;").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/(\s|;|\}|^|\{)out\+='';/g,"$1").replace(/\+''/g,""),d&&(i.selfcontained||!t||t._encodeHTML||(t._encodeHTML=r.encodeHTMLSource(i.doNotSkipEncoded)),f="var encodeHTML = typeof _encodeHTML !== 'undefined' ? _encodeHTML : ("+r.encodeHTMLSource.toString()+"("+(i.doNotSkipEncoded||"")+"));"+f);try{return new Function(i.varname,f)}catch(e){throw"undefined"!=typeof console&&console.log("Could not create a template function: "+f),e}},r.compile=function(e,n){return r.template(e,null,n)}}();

9832
js/photo-sphere-viewer.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

933
js/three.min.js vendored

File diff suppressed because one or more lines are too long

7
js/uevent.min.js vendored
View file

@ -1,7 +0,0 @@
/*!
* uEvent 1.0.0 - to make any js object an event emitter
* Copyright 2011 Jerome Etienne (http://jetienne.com)
* Copyright 2015-2016 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
* Licensed under MIT (http://opensource.org/licenses/MIT)
*/
!function(a,b){"undefined"!=typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define([],b):a.uEvent=b()}(this,function(){"use strict";var a=function(){return!0},b=function(){return!1},c=function(){};return c.Event=function(a,b){var c=a,d=b;Object.defineProperties(this,{type:{get:function(){return c},set:function(a){},enumerable:!0},args:{get:function(){return d},set:function(a){},enumerable:!0}})},c.Event.prototype={constructor:c.Event,isDefaultPrevented:b,isPropagationStopped:b,preventDefault:function(){this.isDefaultPrevented=a},stopPropagation:function(){this.isPropagationStopped=a}},c.prototype={constructor:c,on:function(a,b){if(this.__events=this.__events||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__events[c]=this.__events[c]||[],this.__events[c].push(a[c]));else a.split(" ").forEach(function(a){this.__events[a]=this.__events[a]||[],this.__events[a].push(b)},this);return this},off:function(a,b){if(this.__events=this.__events||{},"object"==typeof a){for(var c in a)if(a.hasOwnProperty(c)&&c in this.__events){var d=this.__events[c].indexOf(a[c]);-1!==d&&this.__events[c].splice(d,1)}}else a?a.split(" ").forEach(function(a){if(a in this.__events)if(b){var c=this.__events[a].indexOf(b);-1!==c&&this.__events[a].splice(c,1)}else this.__events[a].length=0},this):this.__events={};return this},once:function(a,b){if(this.__once=this.__once||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__once[c]=this.__once[c]||[],this.__once[c].push(a[c]));else a.split(" ").forEach(function(a){this.__once[a]=this.__once[a]||[],this.__once[a].push(b)},this);return this},trigger:function(a){var b,d,e,f=Array.prototype.slice.call(arguments,1),g=new c.Event(a,f);if(f.push(g),this.__events&&a in this.__events)for(b=0,d=this.__events[a].length;d>b;b++)if(e=this.__events[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return g;if(this.__once&&a in this.__once){for(b=0,d=this.__once[a].length;d>b;b++)if(e=this.__once[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return delete this.__once[a],g;delete this.__once[a]}return g},change:function(a,b){var d,e,f,g=Array.prototype.slice.call(arguments,1),h=new c.Event(a,g);if(g.push(h),this.__events&&a in this.__events)for(d=0,e=this.__events[a].length;e>d;d++)if(g[0]=b,f=this.__events[a][d],b="object"==typeof f?f.handleEvent(h):f.apply(this,g),h.isPropagationStopped())return b;return b}},c.mixin=function(a,b){b=b||{},a="function"==typeof a?a.prototype:a,["on","off","once","trigger","change"].forEach(function(d){var e=b[d]||d;a[e]=c.prototype[d]}),Object.defineProperties(a,{__events:{value:null,writable:!0},__once:{value:null,writable:!0}})},c});

66
src/main.rs Normal file
View file

@ -0,0 +1,66 @@
use anyhow::{anyhow, bail, Result};
use log::{error, info, warn};
use rfd::FileDialog;
use rust_embed::RustEmbed;
use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode};
use std::path::PathBuf;
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
webview::WebViewBuilder,
};
#[derive(RustEmbed)]
#[folder = "embed/"]
struct Embed;
fn main() -> Result<()> {
// Init log
TermLogger::init(
LevelFilter::Info,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
)?;
let img_path = if let Some(img_path) = std::env::args_os().skip(1).next() {
PathBuf::from(img_path)
} else {
FileDialog::new()
.add_filter("JPG",