Windows ga (#358)
* Enhance Cloud TTS with navigation controls and shared UI overlay in desktop app * Refactor Cloud TTS voice settings and remove standalone settings overlay on desktop app * Persist reader window state and improve slider interaction in desktop app * Improve PDF page transitions and refine focus management in desktop app * Refactor scrollbar interaction and adjust desktop modal focus handling * Improve PDF sidecar synchronization and cross-platform metadata compatibility * Refactor PDF annotation comment logic to shared module and implement Desktop UI * Refactor reader screen to use tap-to-toggle and full-width styling in desktop app * Refactor reader workspace layout and chrome-panel interactions * Implement global search keyboard shortcuts and focusable chrome layers * Implement flavor-specific legal links and update the About UI * Refactor reader UI controls on desktop app * Enhance desktop folder sync with background metadata extraction and improved error handling * Refactor Library UI and remove redundant Home tab in desktop app * Add custom tooltips to reader icon buttons in desktop app * Integrate app theme controls into reader interfaces on desktop * Add right-to-left pagination support and improve focus restoration on desktop app * Improve EPUB pagination geometry and diagnostic logging for layout cutoffs on desktop app * Update desktop reader defaults and implement settings migration * Implement block-based position tracking in ReaderLocator * Enhance EPUB highlighting reliability in desktop app * Add support for custom reader themes and update highlight palette logic in desktop app * Replace the Tools panel with a "More" dropdown menu and refactor account UI * Implement account profile header in desktop sidebar * Implement cloud sync reliability improvements and sidebar toggle on desktop app * Improve EPUB annotation synchronization and highlight mapping accuracy in desktop app * Integrate WebView2 for EPUB vertical rendering on Windows * Refactor reader layout logic and enhance WebView2 diagnostics * Improve vertical reading layout and WebView2 resizing on Desktop * Refine vertical reading mode layout and margin handling * Enhance reader locator precision and Desktop mode-switching reliability * Implement chapter-level caching and warm-start pagination in desktop app * Replace bundled KCEF with native system webviews via SWT * Refactor EPUB page info bar visibility and layout logic * Improve PDF toolbar persistence and fix tab reactivation logic * Enable multi-selection and bulk operations for custom fonts * Refactor instrumentation tests * Add EPUB UI test fixture and initial instrumentation tests * Expand EpubReader UI tests and improve accessibility * Add instrumentation tests and test tags for library and reader screens * Enhance OPDS parser logic and catalog integration * Add support for toggling local synchronization on a per-folder basis. * Implement tri-state sizing for the TTS overlay * Persist TTS overlay size across sessions * Refactor reader brightness control and add incremental step buttons * Improve CSS support, pagination control, and style-aware semantic caching * Improve link handling, interaction, and diagnostics in the paginated reader * crash fixes * Implement persistent pending removal for external files * Implement book-specific word replacements * Add native vertical reading mode with custom renderer * Implement text selection and navigation improvements for the native vertical reader * Implement locator-based navigation and improved vertical scrolling in native vertical mode in epub * Implement lazy loading and chapter prefetching for native vertical reader * Improve window lifecycle and disposal handling on Desktop * Optimize vertical reading performance in desktop app * Enhance TTS start accuracy and diagnostic logging on desktop * Refactor AI settings visibility on desktop * Improve pagination height measurement and enhance cutoff diagnostics * Implement lifecycle management and improve justified text splitting for pagination * Refine AI usage tracking and force AI feature visibility on Desktop * Add descriptive context comments and usage examples to string and plural resources. * Optimize performance and memory usage in search and state mapping * Replace reader page sliders with minimal slider and navigation controls * Add support for CBT comic archives * Harden file path validation and XML parsing to prevent security vulnerabilities * Implement local account profile caching and optimize desktop performance * Improve desktop persistence reliability and add Linux secure storage support * Improved PDF zoom stability and layout prediction during zoom commits * Improved PDF spread layout prediction, reader focus restoration, and account profile caching * Enhance highlight precision and scoping using block-local offsets and CFIs * Enhance cloud book content synchronization and background downloads * Implement granular timestamp tracking for reading positions and PDF annotations * Restrict diagnostic logging and stack traces to debug builds * Refine PDF page gaps and reader chrome interaction logic * Refactor PDF highlight rendering and overhaul Desktop sidebar UI * Implement a new interaction dock and undo/redo history for PDF annotations in desktop * Enhance PDF color picker and improve navigation scroll restoration * Add highlight palette customization and improve selection menu UI in desktop app epub reader * Enhance desktop shelf management and library organization
This commit is contained in:
parent
5971eaa571
commit
83dcafa4b6
444 changed files with 47279 additions and 8096 deletions
|
|
@ -17,49 +17,66 @@ import org.junit.runner.RunWith
|
|||
@RunWith(AndroidJUnit4::class)
|
||||
class CssParserTest {
|
||||
|
||||
private val dummyConstraints = androidx.compose.ui.unit.Constraints()
|
||||
private val baseFontSize = 16f
|
||||
private val density = 1f
|
||||
|
||||
private fun parseTextColor(value: String): Color? {
|
||||
val result = CssParser.parse(
|
||||
cssContent = "p { color: $value; }",
|
||||
cssPath = null,
|
||||
baseFontSizeSp = baseFontSize,
|
||||
density = density,
|
||||
constraints = dummyConstraints,
|
||||
isDarkTheme = false
|
||||
)
|
||||
return result.rules.byTag["p"]
|
||||
?.firstOrNull()
|
||||
?.style
|
||||
?.spanStyle
|
||||
?.color
|
||||
?.takeIf { it.isSpecified }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesNamedColorsCorrectly() {
|
||||
assertThat(CssParser.parseColor("red")).isEqualTo(Color.Red)
|
||||
assertThat(CssParser.parseColor("black")).isEqualTo(Color.Black)
|
||||
assertThat(CssParser.parseColor("transparent")).isEqualTo(Color.Transparent)
|
||||
assertThat(parseTextColor("red")).isEqualTo(Color.Red)
|
||||
assertThat(parseTextColor("black")).isEqualTo(Color.Black)
|
||||
assertThat(parseTextColor("transparent")).isEqualTo(Color.Transparent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles3DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#F0C")).isEqualTo(Color(0xFFFF00CC))
|
||||
assertThat(parseTextColor("#F0C")).isEqualTo(Color(0xFFFF00CC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles6DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#FF00CC")).isEqualTo(Color(0xFFFF00CC))
|
||||
assertThat(parseTextColor("#FF00CC")).isEqualTo(Color(0xFFFF00CC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles8DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#80FF00CC")).isEqualTo(Color(0x80FF00CC))
|
||||
assertThat(parseTextColor("#80FF00CC")).isEqualTo(Color(128, 255, 0, 204))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesRgbFunction() {
|
||||
assertThat(CssParser.parseColor("rgb(255, 0, 204)")).isEqualTo(Color(255, 0, 204))
|
||||
assertThat(parseTextColor("rgb(255, 0, 204)")).isEqualTo(Color(255, 0, 204))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesRgbaFunction() {
|
||||
assertThat(CssParser.parseColor("rgba(255, 0, 204, 0.5)")).isEqualTo(Color(255, 0, 204, 128))
|
||||
assertThat(parseTextColor("rgba(255, 0, 204, 0.5)")).isEqualTo(Color(255, 0, 204, 128))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_returnsNullForInvalidInput() {
|
||||
assertThat(CssParser.parseColor("not a color")).isNull()
|
||||
assertThat(CssParser.parseColor("#12345")).isNull()
|
||||
assertThat(CssParser.parseColor("rgb(1,2)")).isNull()
|
||||
assertThat(parseTextColor("not a color")).isNull()
|
||||
assertThat(parseTextColor("#12345")).isNull()
|
||||
assertThat(parseTextColor("rgb(1,2)")).isNull()
|
||||
}
|
||||
|
||||
private val dummyConstraints = androidx.compose.ui.unit.Constraints()
|
||||
private val baseFontSize = 16f
|
||||
private val density = 1f
|
||||
|
||||
@Test
|
||||
fun parse_handlesSimpleRule() {
|
||||
val css = "p { color: red; }"
|
||||
|
|
@ -120,12 +137,12 @@ class CssParserTest {
|
|||
}
|
||||
p { color: black; }
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, "/some/path/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val result = CssParser.parse(css, "OEBPS/styles/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.rules.byTag).containsKey("p")
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
val fontFace = result.fontFaces.first()
|
||||
assertThat(fontFace.fontFamily).isEqualTo("mycustomfont")
|
||||
assertThat(fontFace.src).isEqualTo("/some/fonts/myfont.ttf")
|
||||
assertThat(fontFace.src).isEqualTo("OEBPS/fonts/myfont.ttf")
|
||||
assertThat(fontFace.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
assertThat(fontFace.fontStyle).isEqualTo(FontStyle.Normal)
|
||||
}
|
||||
|
|
@ -190,10 +207,11 @@ class CssParserTest {
|
|||
val css = "div { border: 2px solid red; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["div"]?.first()?.style?.blockStyle
|
||||
assertThat(style?.border).isNotNull()
|
||||
assertThat(style?.border?.width).isEqualTo(2.dp)
|
||||
assertThat(style?.border?.style).isEqualTo("solid")
|
||||
assertThat(style?.border?.color).isEqualTo(Color.Red)
|
||||
val expectedBorder = BorderStyle(width = 2.dp, color = Color.Red, style = "solid")
|
||||
assertThat(style?.borderTop).isEqualTo(expectedBorder)
|
||||
assertThat(style?.borderRight).isEqualTo(expectedBorder)
|
||||
assertThat(style?.borderBottom).isEqualTo(expectedBorder)
|
||||
assertThat(style?.borderLeft).isEqualTo(expectedBorder)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -262,9 +280,70 @@ class CssParserTest {
|
|||
url("font.ttf") format("truetype");
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, "/css/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val result = CssParser.parse(css, "OEBPS/css/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
assertThat(result.fontFaces.first().src).isEqualTo("/css/font.otf")
|
||||
assertThat(result.fontFaces.first().src).isEqualTo("OEBPS/css/font.otf")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesNestedMediaAndCalcVariables() {
|
||||
val css = """
|
||||
:root { --gap: 12px; }
|
||||
@media screen and (min-width: 300px) {
|
||||
p { margin-left: calc(var(--gap) + 8px); color: hsl(120 100% 25%); }
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = CssParser.parse(
|
||||
css,
|
||||
null,
|
||||
baseFontSize,
|
||||
density,
|
||||
androidx.compose.ui.unit.Constraints(maxWidth = 500),
|
||||
isDarkTheme = false
|
||||
)
|
||||
|
||||
val style = result.rules.byTag["p"]!!.first().style
|
||||
assertThat(style.blockStyle.margin.left).isEqualTo(20.dp)
|
||||
assertThat(style.spanStyle.color).isEqualTo(Color(0, 128, 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_preservesBeforeAfterPseudoElementRules() {
|
||||
val css = "p::before { content: 'Note: '; color: red; } p { color: blue; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
|
||||
val pseudoRule = result.rules.otherComplex.single { it.pseudoElement == "before" }
|
||||
assertThat(pseudoRule.selector.selector).isEqualTo("p")
|
||||
assertThat(pseudoRule.style.content).isEqualTo("'Note: '")
|
||||
assertThat(pseudoRule.style.spanStyle.color).isEqualTo(Color.Red)
|
||||
assertThat(result.rules.byTag["p"]!!.single().style.spanStyle.color).isEqualTo(Color.Blue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesModernRgbSlashAlphaAndCssHexAlpha() {
|
||||
assertThat(parseTextColor("rgb(255 0 204 / 50%)")).isEqualTo(Color(255, 0, 204, 128))
|
||||
assertThat(parseTextColor("#ff00cc80")).isEqualTo(Color(255, 0, 204, 128))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_backgroundShorthandExtractsColorAndImage() {
|
||||
val css = "section { background: #ffeecc url('../images/paper.png') repeat; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["section"]!!.first().style.blockStyle
|
||||
|
||||
assertThat(style.backgroundColor).isEqualTo(Color(255, 238, 204))
|
||||
assertThat(style.backgroundImage).isEqualTo("../images/paper.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_listStyleShorthandExtractsMarkerTypeAndImage() {
|
||||
val css = "ul { list-style: square url('../images/bullet.png') outside; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["ul"]!!.first().style.blockStyle
|
||||
|
||||
assertThat(style.listStyleType).isEqualTo("square")
|
||||
assertThat(style.listStyleImage).isEqualTo("../images/bullet.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -327,10 +406,10 @@ class CssParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun parse_lineHeightClampsSmallEmValues() {
|
||||
fun parse_lineHeightPreservesUnitlessMultiplier() {
|
||||
val css = "p { line-height: 1.1; }" // This is treated as 1.1em
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["p"]?.first()?.style
|
||||
assertThat(style?.paragraphStyle?.lineHeight).isEqualTo(2.0.em)
|
||||
assertThat(style?.paragraphStyle?.lineHeight).isEqualTo(1.1.em)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue