|
@@ -106,22 +106,309 @@
|
|
|
background-color: #1f2937;
|
|
background-color: #1f2937;
|
|
|
color: #f9fafb;
|
|
color: #f9fafb;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- /* 底部按钮栏 */
|
|
|
|
|
- .bottom-bar {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- bottom: 0;
|
|
|
|
|
- margin: auto;
|
|
|
|
|
- width: 75%;
|
|
|
|
|
- left: 50%;
|
|
|
|
|
- transform: translateX(-50%);
|
|
|
|
|
- background-color: rgba(255, 255, 255, 0.0);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-around;
|
|
|
|
|
- padding: 6px 0;
|
|
|
|
|
- box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
|
|
|
|
|
- z-index: 1000;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ :root {
|
|
|
|
|
+ --reader-controls-size: 52px;
|
|
|
|
|
+ --reader-controls-gap: 8px;
|
|
|
|
|
+ --reader-controls-radius: 18px;
|
|
|
|
|
+ --reader-controls-padding: 10px;
|
|
|
|
|
+ --reader-controls-surface: rgba(255, 255, 255, 0.96);
|
|
|
|
|
+ --reader-controls-border: rgba(15, 23, 42, 0.08);
|
|
|
|
|
+ --reader-controls-icon: 20px;
|
|
|
|
|
+ --reader-controls-font: 11px;
|
|
|
|
|
+ --reader-controls-label: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-size-medium {
|
|
|
|
|
+ --reader-controls-size: 60px;
|
|
|
|
|
+ --reader-controls-gap: 10px;
|
|
|
|
|
+ --reader-controls-radius: 20px;
|
|
|
|
|
+ --reader-controls-padding: 12px;
|
|
|
|
|
+ --reader-controls-icon: 22px;
|
|
|
|
|
+ --reader-controls-font: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-size-large {
|
|
|
|
|
+ --reader-controls-size: 68px;
|
|
|
|
|
+ --reader-controls-gap: 12px;
|
|
|
|
|
+ --reader-controls-radius: 22px;
|
|
|
|
|
+ --reader-controls-padding: 14px;
|
|
|
|
|
+ --reader-controls-icon: 24px;
|
|
|
|
|
+ --reader-controls-font: 13px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #mainContainer {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
+ grid-template-rows: auto minmax(0, 1fr);
|
|
|
|
|
+ min-height: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerWorkspace {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: auto minmax(0, 1fr);
|
|
|
|
|
+ grid-template-rows: minmax(0, 1fr);
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ background: linear-gradient(180deg, rgba(248, 250, 252, 0.9), rgba(241, 245, 249, 0.72));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #viewerContainer {
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-right #readerWorkspace {
|
|
|
|
|
+ grid-template-columns: minmax(0, 1fr) auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-top #readerWorkspace,
|
|
|
|
|
+ body.reader-position-bottom #readerWorkspace {
|
|
|
|
|
+ grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
+ grid-template-rows: auto minmax(0, 1fr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-bottom #readerWorkspace {
|
|
|
|
|
+ grid-template-rows: minmax(0, 1fr) auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControls {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: var(--reader-controls-gap);
|
|
|
|
|
+ padding: var(--reader-controls-padding);
|
|
|
|
|
+ background: var(--reader-controls-surface);
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ border-right: 1px solid var(--reader-controls-border);
|
|
|
|
|
+ box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.7);
|
|
|
|
|
+ backdrop-filter: blur(14px);
|
|
|
|
|
+ z-index: 5;
|
|
|
|
|
+ align-self: stretch;
|
|
|
|
|
+ justify-content: flex-start;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-left #readerControls,
|
|
|
|
|
+ body.reader-position-right #readerControls {
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ width: calc(var(--reader-controls-size) + var(--reader-controls-padding) * 2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-right #readerControls {
|
|
|
|
|
+ order: 2;
|
|
|
|
|
+ border-right: none;
|
|
|
|
|
+ border-left: 1px solid var(--reader-controls-border);
|
|
|
|
|
+ box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.7);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-top #readerControls,
|
|
|
|
|
+ body.reader-position-bottom #readerControls {
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ width: auto;
|
|
|
|
|
+ min-height: calc(var(--reader-controls-size) + var(--reader-controls-padding) * 2);
|
|
|
|
|
+ border-right: none;
|
|
|
|
|
+ border-bottom: 1px solid var(--reader-controls-border);
|
|
|
|
|
+ box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.7);
|
|
|
|
|
+ overflow-x: auto;
|
|
|
|
|
+ overflow-y: hidden;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-bottom #readerControls {
|
|
|
|
|
+ order: 2;
|
|
|
|
|
+ border-bottom: none;
|
|
|
|
|
+ border-top: 1px solid var(--reader-controls-border);
|
|
|
|
|
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControls.collapsed {
|
|
|
|
|
+ padding: 10px 8px;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-left #readerControls.collapsed,
|
|
|
|
|
+ body.reader-position-right #readerControls.collapsed {
|
|
|
|
|
+ width: calc(var(--reader-controls-size) + 16px);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControls.collapsed .reader-tool:not(.reader-tool-collapse),
|
|
|
|
|
+ #readerControls.collapsed .reader-settings-launcher {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tools {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: var(--reader-controls-gap);
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-left .reader-tools,
|
|
|
|
|
+ body.reader-position-right .reader-tools {
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-top .reader-tools,
|
|
|
|
|
+ body.reader-position-bottom .reader-tools {
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool,
|
|
|
|
|
+ .reader-settings-launcher {
|
|
|
|
|
+ width: var(--reader-controls-size);
|
|
|
|
|
+ min-width: var(--reader-controls-size);
|
|
|
|
|
+ height: var(--reader-controls-size);
|
|
|
|
|
+ border: 1px solid rgba(15, 23, 42, 0.12);
|
|
|
|
|
+ border-radius: calc(var(--reader-controls-radius) - 4px);
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.88);
|
|
|
|
|
+ color: #0f172a;
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
|
|
|
|
|
+ box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool:hover,
|
|
|
|
|
+ .reader-settings-launcher:hover {
|
|
|
|
|
+ background: #f8fafc;
|
|
|
|
|
+ border-color: rgba(15, 23, 42, 0.2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool:active,
|
|
|
|
|
+ .reader-settings-launcher:active {
|
|
|
|
|
+ transform: translateY(1px);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool.is-active {
|
|
|
|
|
+ background: #e2e8f0;
|
|
|
|
|
+ border-color: rgba(15, 23, 42, 0.22);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool.reader-tool-danger {
|
|
|
|
|
+ color: #b91c1c;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool.reader-tool-accent {
|
|
|
|
|
+ color: #166534;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool svg,
|
|
|
|
|
+ .reader-settings-launcher svg {
|
|
|
|
|
+ width: var(--reader-controls-icon);
|
|
|
|
|
+ height: var(--reader-controls-icon);
|
|
|
|
|
+ stroke: currentColor;
|
|
|
|
|
+ fill: none;
|
|
|
|
|
+ stroke-width: 1.8;
|
|
|
|
|
+ stroke-linecap: round;
|
|
|
|
|
+ stroke-linejoin: round;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool-label {
|
|
|
|
|
+ display: var(--reader-controls-label);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-tool-stack {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+ font-size: var(--reader-controls-font);
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ letter-spacing: 0.02em;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControlsSpacer {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 8px;
|
|
|
|
|
+ min-height: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-left #readerControlsSpacer,
|
|
|
|
|
+ body.reader-position-right #readerControlsSpacer {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-panel {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ inset: auto auto 12px 12px;
|
|
|
|
|
+ width: min(320px, calc(100vw - 24px));
|
|
|
|
|
+ padding: 14px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.98);
|
|
|
|
|
+ border: 1px solid rgba(15, 23, 42, 0.1);
|
|
|
|
|
+ border-radius: 18px;
|
|
|
|
|
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.16);
|
|
|
|
|
+ z-index: 20;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControls.settings-open .reader-settings-panel {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-right .reader-settings-panel {
|
|
|
|
|
+ inset: auto 12px 12px auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-top .reader-settings-panel {
|
|
|
|
|
+ inset: 12px auto auto 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.reader-position-bottom .reader-settings-panel {
|
|
|
|
|
+ inset: auto auto 12px 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-title {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+ color: #0f172a;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-group {
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-group:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-label {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #475569;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ text-transform: uppercase;
|
|
|
|
|
+ letter-spacing: 0.06em;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-options {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-option {
|
|
|
|
|
+ border: 1px solid rgba(15, 23, 42, 0.12);
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #0f172a;
|
|
|
|
|
+ padding: 6px 10px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-option.is-selected {
|
|
|
|
|
+ background: #0f172a;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border-color: #0f172a;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/* 顶部状态条 */
|
|
/* 顶部状态条 */
|
|
|
#loading-indicator {
|
|
#loading-indicator {
|
|
@@ -240,12 +527,40 @@
|
|
|
body.night-reading-mode #loading-indicator,
|
|
body.night-reading-mode #loading-indicator,
|
|
|
body.night-reading-mode .user-menu-trigger,
|
|
body.night-reading-mode .user-menu-trigger,
|
|
|
body.night-reading-mode .user-menu-panel,
|
|
body.night-reading-mode .user-menu-panel,
|
|
|
- body.night-reading-mode .modal {
|
|
|
|
|
|
|
+ body.night-reading-mode .modal,
|
|
|
|
|
+ body.night-reading-mode #readerControls,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-panel {
|
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.45);
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.45);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- body.night-reading-mode .bottom-bar {
|
|
|
|
|
- background-color: rgba(15, 23, 42, 0.18);
|
|
|
|
|
|
|
+ body.night-reading-mode #readerWorkspace {
|
|
|
|
|
+ background: linear-gradient(180deg, rgba(15, 23, 42, 0.98), rgba(15, 23, 42, 0.9));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.night-reading-mode #readerControls,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-panel {
|
|
|
|
|
+ background: rgba(15, 23, 42, 0.96);
|
|
|
|
|
+ border-color: rgba(148, 163, 184, 0.16);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.night-reading-mode .reader-tool,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-launcher,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-option {
|
|
|
|
|
+ background: rgba(30, 41, 59, 0.92);
|
|
|
|
|
+ color: #e2e8f0;
|
|
|
|
|
+ border-color: rgba(148, 163, 184, 0.16);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.night-reading-mode .reader-tool.is-active,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-option.is-selected {
|
|
|
|
|
+ background: #e2e8f0;
|
|
|
|
|
+ color: #0f172a;
|
|
|
|
|
+ border-color: #e2e8f0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ body.night-reading-mode .reader-settings-title,
|
|
|
|
|
+ body.night-reading-mode .reader-settings-label {
|
|
|
|
|
+ color: #e2e8f0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
body.night-reading-mode .pdfViewer .page {
|
|
body.night-reading-mode .pdfViewer .page {
|
|
@@ -259,21 +574,26 @@
|
|
|
padding: 10px;
|
|
padding: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .button {
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- padding: 4px 4px;
|
|
|
|
|
- margin: 0 2px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .bottom-bar {
|
|
|
|
|
- padding: 6px 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- #loading-indicator {
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- padding: 3px 5px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .button {
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ padding: 4px 4px;
|
|
|
|
|
+ margin: 0 2px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #readerControls {
|
|
|
|
|
+ --reader-controls-padding: 8px;
|
|
|
|
|
+ --reader-controls-gap: 6px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #loading-indicator {
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ padding: 3px 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reader-settings-panel {
|
|
|
|
|
+ width: min(280px, calc(100vw - 20px));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
</head>
|
|
</head>
|
|
|
|
|
|
|
@@ -287,17 +607,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <audio id="audio-player" controls></audio>
|
|
|
|
|
-
|
|
|
|
|
- <div class="bottom-bar">
|
|
|
|
|
- <button id="uploadButton" class="button">上传 PDF</button>
|
|
|
|
|
- <button id="openDirectoryButton" class="button">打开目录</button>
|
|
|
|
|
- <button id="nightModeButton" class="button button-night">夜间模式</button>
|
|
|
|
|
- <button id="select-all-text" class="button button-light">阅读整页</button>
|
|
|
|
|
- <button id="pause-play-button" class="button button-pause">暂停播放</button>
|
|
|
|
|
- <button id="stop-play-button" class="button button-stop">停止</button>
|
|
|
|
|
- <button id="toggle-text-select" class="button button-success">文本选择</button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <audio id="audio-player" controls></audio>
|
|
|
|
|
|
|
|
<input type="file" id="pdfInput" accept="application/pdf" style="display: none;" />
|
|
<input type="file" id="pdfInput" accept="application/pdf" style="display: none;" />
|
|
|
|
|
|
|
@@ -374,9 +684,9 @@
|
|
|
<div id="sidebarResizer"></div>
|
|
<div id="sidebarResizer"></div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div id="mainContainer">
|
|
|
|
|
- <div class="toolbar">
|
|
|
|
|
- <div id="toolbarContainer">
|
|
|
|
|
|
|
+ <div id="mainContainer">
|
|
|
|
|
+ <div class="toolbar">
|
|
|
|
|
+ <div id="toolbarContainer">
|
|
|
<div id="toolbarViewer" class="toolbarHorizontalGroup">
|
|
<div id="toolbarViewer" class="toolbarHorizontalGroup">
|
|
|
<div id="toolbarViewerLeft" class="toolbarHorizontalGroup">
|
|
<div id="toolbarViewerLeft" class="toolbarHorizontalGroup">
|
|
|
<button id="sidebarToggleButton" class="toolbarButton" type="button"
|
|
<button id="sidebarToggleButton" class="toolbarButton" type="button"
|
|
@@ -815,9 +1125,87 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div id="viewerContainer" tabindex="0">
|
|
|
|
|
- <div id="viewer" class="pdfViewer"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div id="readerWorkspace">
|
|
|
|
|
+ <aside id="readerControls" class="reader-controls" aria-label="阅读控制栏">
|
|
|
|
|
+ <button id="readerControlsCollapse" class="reader-tool reader-tool-collapse" type="button" title="折叠或展开控制栏" aria-label="折叠或展开控制栏">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 6l-6 6 6 6"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">收起</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <div class="reader-tools">
|
|
|
|
|
+ <button id="uploadButton" class="reader-tool" type="button" title="上传 PDF" aria-label="上传 PDF">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 16V4"/><path d="M8 8l4-4 4 4"/><path d="M4 20h16"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">上传</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="openDirectoryButton" class="reader-tool" type="button" title="打开目录" aria-label="打开目录">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 7h5l2 2h11v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M3 7V5a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v2"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">目录</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="nightModeButton" class="reader-tool" type="button" title="夜间模式" aria-label="夜间模式">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">夜间</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="select-all-text" class="reader-tool" type="button" title="阅读整页" aria-label="阅读整页">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 4h9l3 3v13H6z"/><path d="M15 4v4h4"/><path d="M9 12h6"/><path d="M9 16h6"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">整页</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="pause-play-button" class="reader-tool" type="button" title="暂停播放" aria-label="暂停播放">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 5v14"/><path d="M15 5v14"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">暂停</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="stop-play-button" class="reader-tool reader-tool-danger" type="button" title="停止播放" aria-label="停止播放">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7h10v10H7z"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">停止</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button id="toggle-text-select" class="reader-tool reader-tool-accent" type="button" title="文本选择" aria-label="文本选择">
|
|
|
|
|
+ <span class="reader-tool-stack">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7V4h3"/><path d="M20 7V4h-3"/><path d="M4 17v3h3"/><path d="M20 17v3h-3"/><path d="M8 12h8"/></svg>
|
|
|
|
|
+ <span class="reader-tool-label">选读</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div id="readerControlsSpacer"></div>
|
|
|
|
|
+ <button id="readerSettingsButton" class="reader-settings-launcher" type="button" title="设置" aria-label="设置" aria-expanded="false">
|
|
|
|
|
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v3"/><path d="M12 18v3"/><path d="M3 12h3"/><path d="M18 12h3"/><path d="M5.6 5.6l2.1 2.1"/><path d="M16.3 16.3l2.1 2.1"/><path d="M18.4 5.6l-2.1 2.1"/><path d="M7.7 16.3l-2.1 2.1"/><circle cx="12" cy="12" r="3.5"/></svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <div id="readerSettingsPanel" class="reader-settings-panel">
|
|
|
|
|
+ <div class="reader-settings-title">阅读栏设置</div>
|
|
|
|
|
+ <div class="reader-settings-group">
|
|
|
|
|
+ <label class="reader-settings-label">位置</label>
|
|
|
|
|
+ <div class="reader-settings-options" data-setting-group="position">
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-position="left">左侧</button>
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-position="right">右侧</button>
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-position="top">顶部</button>
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-position="bottom">底部</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="reader-settings-group">
|
|
|
|
|
+ <label class="reader-settings-label">尺寸</label>
|
|
|
|
|
+ <div class="reader-settings-options" data-setting-group="size">
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-size="small">小</button>
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-size="medium">中</button>
|
|
|
|
|
+ <button class="reader-settings-option" type="button" data-size="large">大</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </aside>
|
|
|
|
|
+ <div id="viewerContainer" tabindex="0">
|
|
|
|
|
+ <div id="viewer" class="pdfViewer"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div id="dialogContainer">
|
|
<div id="dialogContainer">
|
|
@@ -1154,26 +1542,82 @@
|
|
|
const pausePlayButton = document.getElementById('pause-play-button');
|
|
const pausePlayButton = document.getElementById('pause-play-button');
|
|
|
const stopPlayButton = document.getElementById('stop-play-button');
|
|
const stopPlayButton = document.getElementById('stop-play-button');
|
|
|
const nightModeButton = document.getElementById('nightModeButton');
|
|
const nightModeButton = document.getElementById('nightModeButton');
|
|
|
- const bottomBar = document.querySelector('.bottom-bar');
|
|
|
|
|
const userMenu = document.getElementById('userMenu');
|
|
const userMenu = document.getElementById('userMenu');
|
|
|
const userMenuTrigger = document.getElementById('userMenuTrigger');
|
|
const userMenuTrigger = document.getElementById('userMenuTrigger');
|
|
|
const userMenuPanel = document.getElementById('userMenuPanel');
|
|
const userMenuPanel = document.getElementById('userMenuPanel');
|
|
|
const userLogoutBtn = document.getElementById('userLogoutBtn');
|
|
const userLogoutBtn = document.getElementById('userLogoutBtn');
|
|
|
- const userAdminBtn = document.getElementById('userAdminBtn');
|
|
|
|
|
-
|
|
|
|
|
- let selectedFile = null;
|
|
|
|
|
- let currentPage = 1;
|
|
|
|
|
- const itemsPerPage = 10;
|
|
|
|
|
|
|
+ const userAdminBtn = document.getElementById('userAdminBtn');
|
|
|
|
|
+ const readerControls = document.getElementById('readerControls');
|
|
|
|
|
+ const readerControlsCollapse = document.getElementById('readerControlsCollapse');
|
|
|
|
|
+ const readerSettingsButton = document.getElementById('readerSettingsButton');
|
|
|
|
|
+ const readerSettingsPanel = document.getElementById('readerSettingsPanel');
|
|
|
|
|
+
|
|
|
|
|
+ let selectedFile = null;
|
|
|
|
|
+ let currentPage = 1;
|
|
|
|
|
+ const itemsPerPage = 10;
|
|
|
let allFiles = [];
|
|
let allFiles = [];
|
|
|
let selectedStartContext = null;
|
|
let selectedStartContext = null;
|
|
|
const currentFilePath = new URLSearchParams(window.location.search).get('file') || '';
|
|
const currentFilePath = new URLSearchParams(window.location.search).get('file') || '';
|
|
|
let isRestoringProgress = false;
|
|
let isRestoringProgress = false;
|
|
|
const NIGHT_MODE_STORAGE_KEY = 'reader_pro.night_mode';
|
|
const NIGHT_MODE_STORAGE_KEY = 'reader_pro.night_mode';
|
|
|
|
|
+ const READER_POSITION_STORAGE_KEY = 'reader_pro.controls.position';
|
|
|
|
|
+ const READER_SIZE_STORAGE_KEY = 'reader_pro.controls.size';
|
|
|
|
|
+ const READER_COLLAPSED_STORAGE_KEY = 'reader_pro.controls.collapsed';
|
|
|
|
|
+
|
|
|
|
|
+ function updateReaderToggleIcon() {
|
|
|
|
|
+ const icon = readerControlsCollapse.querySelector('svg path');
|
|
|
|
|
+ const position = document.body.dataset.readerPosition || 'left';
|
|
|
|
|
+ const collapsed = readerControls.classList.contains('collapsed');
|
|
|
|
|
+ const directionMap = {
|
|
|
|
|
+ left: collapsed ? 'M9 6l6 6-6 6' : 'M15 6l-6 6 6 6',
|
|
|
|
|
+ right: collapsed ? 'M15 6l-6 6 6 6' : 'M9 6l6 6-6 6',
|
|
|
|
|
+ top: collapsed ? 'M6 15l6-6 6 6' : 'M6 9l6 6 6-6',
|
|
|
|
|
+ bottom: collapsed ? 'M6 9l6 6 6-6' : 'M6 15l6-6 6 6'
|
|
|
|
|
+ };
|
|
|
|
|
+ icon.setAttribute('d', directionMap[position] || directionMap.left);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function setReaderControlsCollapsed(collapsed) {
|
|
|
|
|
+ readerControls.classList.toggle('collapsed', collapsed);
|
|
|
|
|
+ readerControlsCollapse.setAttribute('aria-expanded', String(!collapsed));
|
|
|
|
|
+ localStorage.setItem(READER_COLLAPSED_STORAGE_KEY, collapsed ? '1' : '0');
|
|
|
|
|
+ updateReaderToggleIcon();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function setReaderControlsPosition(position) {
|
|
|
|
|
+ const normalized = ['left', 'right', 'top', 'bottom'].includes(position) ? position : 'left';
|
|
|
|
|
+ document.body.classList.remove('reader-position-left', 'reader-position-right', 'reader-position-top', 'reader-position-bottom');
|
|
|
|
|
+ document.body.classList.add(`reader-position-${normalized}`);
|
|
|
|
|
+ document.body.dataset.readerPosition = normalized;
|
|
|
|
|
+ localStorage.setItem(READER_POSITION_STORAGE_KEY, normalized);
|
|
|
|
|
+ document.querySelectorAll('[data-position]').forEach(button => {
|
|
|
|
|
+ button.classList.toggle('is-selected', button.dataset.position === normalized);
|
|
|
|
|
+ });
|
|
|
|
|
+ updateReaderToggleIcon();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function setReaderControlsSize(size) {
|
|
|
|
|
+ const normalized = ['small', 'medium', 'large'].includes(size) ? size : 'small';
|
|
|
|
|
+ document.body.classList.remove('reader-size-small', 'reader-size-medium', 'reader-size-large');
|
|
|
|
|
+ document.body.classList.add(`reader-size-${normalized}`);
|
|
|
|
|
+ document.body.dataset.readerSize = normalized;
|
|
|
|
|
+ localStorage.setItem(READER_SIZE_STORAGE_KEY, normalized);
|
|
|
|
|
+ document.querySelectorAll('[data-size]').forEach(button => {
|
|
|
|
|
+ button.classList.toggle('is-selected', button.dataset.size === normalized);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function initReaderControls() {
|
|
|
|
|
+ const defaultPosition = window.innerWidth <= 768 ? 'bottom' : 'left';
|
|
|
|
|
+ setReaderControlsPosition(localStorage.getItem(READER_POSITION_STORAGE_KEY) || defaultPosition);
|
|
|
|
|
+ setReaderControlsSize(localStorage.getItem(READER_SIZE_STORAGE_KEY) || 'small');
|
|
|
|
|
+ setReaderControlsCollapsed(localStorage.getItem(READER_COLLAPSED_STORAGE_KEY) === '1');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
function updateNightModeButton(isEnabled) {
|
|
function updateNightModeButton(isEnabled) {
|
|
|
- nightModeButton.textContent = isEnabled ? '日间模式' : '夜间模式';
|
|
|
|
|
- nightModeButton.classList.toggle('button-light', isEnabled);
|
|
|
|
|
- nightModeButton.classList.toggle('button-night', !isEnabled);
|
|
|
|
|
|
|
+ nightModeButton.classList.toggle('is-active', isEnabled);
|
|
|
|
|
+ nightModeButton.title = isEnabled ? '日间模式' : '夜间模式';
|
|
|
|
|
+ nightModeButton.setAttribute('aria-label', isEnabled ? '日间模式' : '夜间模式');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function setNightMode(enabled) {
|
|
function setNightMode(enabled) {
|
|
@@ -1245,64 +1689,38 @@
|
|
|
window.location.href = '/admin';
|
|
window.location.href = '/admin';
|
|
|
});
|
|
});
|
|
|
initNightMode();
|
|
initNightMode();
|
|
|
|
|
+ initReaderControls();
|
|
|
nightModeButton.addEventListener('click', function () {
|
|
nightModeButton.addEventListener('click', function () {
|
|
|
const nextEnabled = !document.body.classList.contains('night-reading-mode');
|
|
const nextEnabled = !document.body.classList.contains('night-reading-mode');
|
|
|
setNightMode(nextEnabled);
|
|
setNightMode(nextEnabled);
|
|
|
window.location.reload();
|
|
window.location.reload();
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
- function syncBottomBarToPage() {
|
|
|
|
|
- if (!bottomBar) return;
|
|
|
|
|
-
|
|
|
|
|
- const app = window.PDFViewerApplication;
|
|
|
|
|
- const currentPageNumber = app && app.pdfViewer ? app.pdfViewer.currentPageNumber : null;
|
|
|
|
|
- const pageEl = currentPageNumber
|
|
|
|
|
- ? document.querySelector(`.page[data-page-number="${currentPageNumber}"]`)
|
|
|
|
|
- : document.querySelector('.page');
|
|
|
|
|
-
|
|
|
|
|
- if (!pageEl) {
|
|
|
|
|
- bottomBar.style.width = '75%';
|
|
|
|
|
- bottomBar.style.left = '50%';
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const rect = pageEl.getBoundingClientRect();
|
|
|
|
|
- if (!rect.width) return;
|
|
|
|
|
-
|
|
|
|
|
- const viewportWidth = window.innerWidth;
|
|
|
|
|
- const maxWidth = Math.max(240, viewportWidth - 16);
|
|
|
|
|
- const width = Math.min(rect.width, maxWidth);
|
|
|
|
|
- bottomBar.style.width = `${Math.round(width)}px`;
|
|
|
|
|
-
|
|
|
|
|
- let centerX = rect.left + rect.width / 2;
|
|
|
|
|
- const minCenter = width / 2 + 8;
|
|
|
|
|
- const maxCenter = viewportWidth - width / 2 - 8;
|
|
|
|
|
- centerX = Math.min(maxCenter, Math.max(minCenter, centerX));
|
|
|
|
|
- bottomBar.style.left = `${Math.round(centerX)}px`;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- function setupBottomBarSync() {
|
|
|
|
|
- const viewerContainer = document.getElementById('viewerContainer');
|
|
|
|
|
- if (viewerContainer) {
|
|
|
|
|
- viewerContainer.addEventListener('scroll', () => requestAnimationFrame(syncBottomBarToPage), { passive: true });
|
|
|
|
|
- }
|
|
|
|
|
- window.addEventListener('resize', () => requestAnimationFrame(syncBottomBarToPage), { passive: true });
|
|
|
|
|
-
|
|
|
|
|
- const tryBindPdfEvents = () => {
|
|
|
|
|
- const app = window.PDFViewerApplication;
|
|
|
|
|
- const eventBus = app ? app.eventBus : null;
|
|
|
|
|
- if (!eventBus) {
|
|
|
|
|
- setTimeout(tryBindPdfEvents, 200);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- ['pagechanging', 'scalechanging', 'updateviewarea', 'pagerendered'].forEach(evt => {
|
|
|
|
|
- eventBus.on(evt, () => requestAnimationFrame(syncBottomBarToPage));
|
|
|
|
|
- });
|
|
|
|
|
- requestAnimationFrame(syncBottomBarToPage);
|
|
|
|
|
- };
|
|
|
|
|
- tryBindPdfEvents();
|
|
|
|
|
- }
|
|
|
|
|
- setupBottomBarSync();
|
|
|
|
|
|
|
+ readerControlsCollapse.addEventListener('click', () => {
|
|
|
|
|
+ setReaderControlsCollapsed(!readerControls.classList.contains('collapsed'));
|
|
|
|
|
+ });
|
|
|
|
|
+ readerSettingsButton.addEventListener('click', event => {
|
|
|
|
|
+ event.stopPropagation();
|
|
|
|
|
+ const nextOpen = !readerControls.classList.contains('settings-open');
|
|
|
|
|
+ readerControls.classList.toggle('settings-open', nextOpen);
|
|
|
|
|
+ readerSettingsButton.setAttribute('aria-expanded', String(nextOpen));
|
|
|
|
|
+ });
|
|
|
|
|
+ document.addEventListener('click', event => {
|
|
|
|
|
+ if (!readerControls.contains(event.target)) {
|
|
|
|
|
+ readerControls.classList.remove('settings-open');
|
|
|
|
|
+ readerSettingsButton.setAttribute('aria-expanded', 'false');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ readerSettingsPanel.querySelectorAll('[data-position]').forEach(button => {
|
|
|
|
|
+ button.addEventListener('click', () => setReaderControlsPosition(button.dataset.position));
|
|
|
|
|
+ });
|
|
|
|
|
+ readerSettingsPanel.querySelectorAll('[data-size]').forEach(button => {
|
|
|
|
|
+ button.addEventListener('click', () => setReaderControlsSize(button.dataset.size));
|
|
|
|
|
+ });
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ if (!localStorage.getItem(READER_POSITION_STORAGE_KEY)) {
|
|
|
|
|
+ setReaderControlsPosition(window.innerWidth <= 768 ? 'bottom' : 'left');
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { passive: true });
|
|
|
|
|
|
|
|
async function saveReadingProgress(pageNumber) {
|
|
async function saveReadingProgress(pageNumber) {
|
|
|
if (!currentFilePath || !Number.isInteger(pageNumber) || pageNumber < 1) return;
|
|
if (!currentFilePath || !Number.isInteger(pageNumber) || pageNumber < 1) return;
|
|
@@ -1577,22 +1995,25 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 切换文本选择监听
|
|
// 切换文本选择监听
|
|
|
- document.getElementById('toggle-text-select').addEventListener('click', () => {
|
|
|
|
|
- isListening = !isListening;
|
|
|
|
|
- const button = document.getElementById('toggle-text-select');
|
|
|
|
|
- button.textContent = isListening ? "关闭监听" : "文本选择";
|
|
|
|
|
- button.classList.toggle('button-danger', isListening);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ document.getElementById('toggle-text-select').addEventListener('click', () => {
|
|
|
|
|
+ isListening = !isListening;
|
|
|
|
|
+ const button = document.getElementById('toggle-text-select');
|
|
|
|
|
+ button.classList.toggle('is-active', isListening);
|
|
|
|
|
+ button.title = isListening ? '关闭监听' : '文本选择';
|
|
|
|
|
+ button.setAttribute('aria-label', isListening ? '关闭监听' : '文本选择');
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
// 暂停/继续播放
|
|
// 暂停/继续播放
|
|
|
- pausePlayButton.addEventListener('click', () => {
|
|
|
|
|
- isPaused = !isPaused;
|
|
|
|
|
- pausePlayButton.textContent = isPaused ? "继续播放" : "暂停播放";
|
|
|
|
|
- if (isPaused && currentAudio) {
|
|
|
|
|
- currentAudio.pause();
|
|
|
|
|
- if (audioContext) audioContext.suspend();
|
|
|
|
|
- } else if (!isPaused && currentAudio) {
|
|
|
|
|
- currentAudio.play();
|
|
|
|
|
|
|
+ pausePlayButton.addEventListener('click', () => {
|
|
|
|
|
+ isPaused = !isPaused;
|
|
|
|
|
+ pausePlayButton.classList.toggle('is-active', isPaused);
|
|
|
|
|
+ pausePlayButton.title = isPaused ? '继续播放' : '暂停播放';
|
|
|
|
|
+ pausePlayButton.setAttribute('aria-label', isPaused ? '继续播放' : '暂停播放');
|
|
|
|
|
+ if (isPaused && currentAudio) {
|
|
|
|
|
+ currentAudio.pause();
|
|
|
|
|
+ if (audioContext) audioContext.suspend();
|
|
|
|
|
+ } else if (!isPaused && currentAudio) {
|
|
|
|
|
+ currentAudio.play();
|
|
|
if (audioContext) audioContext.resume();
|
|
if (audioContext) audioContext.resume();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -1615,12 +2036,14 @@
|
|
|
currentAudio.pause();
|
|
currentAudio.pause();
|
|
|
currentAudio.src = '';
|
|
currentAudio.src = '';
|
|
|
currentAudio = null;
|
|
currentAudio = null;
|
|
|
- }
|
|
|
|
|
- isPaused = false;
|
|
|
|
|
- pausePlayButton.textContent = "暂停播放";
|
|
|
|
|
- if (showTip) {
|
|
|
|
|
- const loadingIndicator = document.getElementById('loading-indicator');
|
|
|
|
|
- loadingIndicator.textContent = '已停止';
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ isPaused = false;
|
|
|
|
|
+ pausePlayButton.classList.remove('is-active');
|
|
|
|
|
+ pausePlayButton.title = '暂停播放';
|
|
|
|
|
+ pausePlayButton.setAttribute('aria-label', '暂停播放');
|
|
|
|
|
+ if (showTip) {
|
|
|
|
|
+ const loadingIndicator = document.getElementById('loading-indicator');
|
|
|
|
|
+ loadingIndicator.textContent = '已停止';
|
|
|
loadingIndicator.style.display = 'block';
|
|
loadingIndicator.style.display = 'block';
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
loadingIndicator.style.display = 'none';
|
|
loadingIndicator.style.display = 'none';
|
|
@@ -1699,10 +2122,11 @@
|
|
|
return { word, occurrence };
|
|
return { word, occurrence };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function updateReadPageButtonText() {
|
|
|
|
|
- selectedStartContext = getSelectedContextOnPage();
|
|
|
|
|
- readPageButton.textContent = selectedStartContext ? '从此阅读' : '阅读整页';
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ function updateReadPageButtonText() {
|
|
|
|
|
+ selectedStartContext = getSelectedContextOnPage();
|
|
|
|
|
+ readPageButton.title = selectedStartContext ? '从此阅读' : '阅读整页';
|
|
|
|
|
+ readPageButton.setAttribute('aria-label', selectedStartContext ? '从此阅读' : '阅读整页');
|
|
|
|
|
+ }
|
|
|
document.addEventListener('selectionchange', updateReadPageButtonText);
|
|
document.addEventListener('selectionchange', updateReadPageButtonText);
|
|
|
updateReadPageButtonText();
|
|
updateReadPageButtonText();
|
|
|
|
|
|