<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NodeLane</title>
    <description>The latest articles on DEV Community by NodeLane (@_096282f0dc6decf9962b4c).</description>
    <link>https://dev.to/_096282f0dc6decf9962b4c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4144378%2Fc8a88afb-2099-4c48-84e7-ca031c33c6fc.png</url>
      <title>DEV Community: NodeLane</title>
      <link>https://dev.to/_096282f0dc6decf9962b4c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_096282f0dc6decf9962b4c"/>
    <language>en</language>
    <item>
      <title>Bringing a desktop comic reader extension to Android: the interaction changes</title>
      <dc:creator>NodeLane</dc:creator>
      <pubDate>Sun, 11 Oct 2026 03:12:32 +0000</pubDate>
      <link>https://dev.to/_096282f0dc6decf9962b4c/bringing-a-desktop-comic-reader-extension-to-android-the-interaction-changes-1p9p</link>
      <guid>https://dev.to/_096282f0dc6decf9962b4c/bringing-a-desktop-comic-reader-extension-to-android-the-interaction-changes-1p9p</guid>
      <description>&lt;p&gt;A desktop comic reader can fit inside a phone viewport and still be awkward to use. Buttons may be technically visible but hard to hit, settings can cover the page, and a two-column comparison can make both versions unreadable.&lt;/p&gt;

&lt;p&gt;I'm building &lt;a href="https://github.com/Wy2926/node-comics" rel="noopener noreferrer"&gt;NodeLane Comics&lt;/a&gt;, an open-source browser extension. It now has an Android Firefox entry point. This is a follow-up about the interaction decisions behind that work, rather than another product introduction.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A narrow window is not necessarily a phone
&lt;/h2&gt;

&lt;p&gt;I wanted to keep the existing React components and reading workflow. A second mobile application tree would also mean a second place to maintain imports, translation state, and reading-position recovery.&lt;/p&gt;

&lt;p&gt;The phone stylesheet is loaded after the desktop styles. Its outer gate combines a bounded device width with a coarse pointer or lack of hover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-device-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;coarse&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-device-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700px&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.nc-app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;--nc-phone-gutter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--nc-phone-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--nc-phone-hit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;44px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the gate used by this project, not a universal device-detection recipe. The intention is to keep a narrow desktop window, a desktop extension popup, and a touch-enabled laptop from automatically receiving the full phone layout.&lt;/p&gt;

&lt;p&gt;The compact landscape case matters too: a phone rotated sideways has more width, but very little height.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Wy2926/node-comics/blob/main/apps/extension/src/ui/phone.css" rel="noopener noreferrer"&gt;Implementation: phone.css&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Touch targets need space; decoration can give some back
&lt;/h2&gt;

&lt;p&gt;The mobile layout reduces panel padding and visual decoration without shrinking the controls people need to hit. Reader controls use 44px targets, and touch-oriented inputs keep a 16px minimum font size.&lt;/p&gt;

&lt;p&gt;Navigation and page progress sit above the comic. Reading and translation controls sit below it. The directory and reading settings become full-screen drawers; translation settings use a bottom panel.&lt;/p&gt;

&lt;p&gt;One small but important detail: hiding the controls should not change the comic's position. The reader keeps the relevant canvas space reserved, so bringing the controls back does not move the content underneath them. A tap can reveal tools; finger scrolling should not trigger desktop mouse-edge behavior.&lt;/p&gt;

&lt;p&gt;This is less about making everything large and more about deciding what deserves the limited space.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Original/translation comparison should preserve readable text
&lt;/h2&gt;

&lt;p&gt;On a desktop, two pages side by side are useful. On a narrow phone, squeezing each page into half the screen can defeat the point of comparison.&lt;/p&gt;

&lt;p&gt;The narrow-screen layout preserves a readable page width and allows horizontal viewing instead. Switching back to the original remains available, and the existing reading-position restoration is retained.&lt;/p&gt;

&lt;p&gt;I did not add a separate mobile translation workflow. The UI still consumes the same page-level results and failures. A layout change should not silently become a second implementation of task submission or recovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. File import is a browser-and-OS interaction
&lt;/h2&gt;

&lt;p&gt;The starting point remains a normal file input, not a custom imitation of the Android picker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;
  &lt;span class="na"&gt;multiple&lt;/span&gt;
  &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;COMIC_ACCEPT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;chooseFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shortened excerpt shows the existing import path. The supported collection includes CBZ/ZIP, CBR/RAR, PDF, and DRM-free MOBI/EPUB; the extension is not a loose-image organizer.&lt;/p&gt;

&lt;p&gt;For a first mobile check, a small known CBZ is more useful than a huge archive. It separates basic picker/import problems from memory pressure, parsing time, and unusually complex files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqd2ucufxtuu610h0z6jd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqd2ucufxtuu610h0z6jd.png" alt="Original sample comic opened in Firefox on an Android emulator" width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Evidence boundary: this is an existing tutorial capture from Android 14 and Firefox 157.0.1, with a temporarily loaded Mozilla-signed 0.10.3 extension. The project's original sample was packaged as a one-page CBZ and imported through the real file picker. It illustrates the Android reading path, not the latest layout or verified permanent store installation, sign-in, or translation.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Missing desktop APIs must not prevent the reader from starting
&lt;/h2&gt;

&lt;p&gt;Responsive CSS does not solve browser-extension compatibility.&lt;/p&gt;

&lt;p&gt;Firefox Android may lack desktop capabilities such as commands, context menus, or separate browser windows. Initialization must tolerate those missing capabilities. For the Drive connection flow, the implementation can use a normal tab instead of requiring a separate window, while retaining the existing authorization message bridge.&lt;/p&gt;

&lt;p&gt;The same principle applies to image handling: where OffscreenCanvas is unavailable, the extension has a native canvas fallback. That is a capability fallback, not permission to assume that authorization or processing succeeded.&lt;/p&gt;

&lt;p&gt;A practical self-hosting detail belongs in the mobile instructions as well: localhost on a phone is the phone, not the computer running a translation service.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Keep resource use bounded, and name what a test proves
&lt;/h2&gt;

&lt;p&gt;A comic reader can contain many large images. The mobile work keeps the existing bounded image window and virtualized shelf instead of rendering or decoding an entire library at once. The stylesheet does not add viewport polling or a second set of data requests.&lt;/p&gt;

&lt;p&gt;I do not have a before/after device performance benchmark to claim here. Preserving those limits is a design constraint, not proof that every phone will handle a large PDF equally well.&lt;/p&gt;

&lt;p&gt;There are also several different kinds of validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A narrow-screen browser test can check layout, target sizes, and position restoration.&lt;/li&gt;
&lt;li&gt;An Android emulator can exercise the actual browser and file picker.&lt;/li&gt;
&lt;li&gt;A physical device is still needed to check its installation path, keyboard, background behavior, authorization callbacks, and real reading workload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those should not be collapsed into a single “mobile works” checkbox. iOS / Orion remains compatibility work in progress, not a supported platform announcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the reading path
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://comics.nodelane.net/en/guides/android-firefox/" rel="noopener noreferrer"&gt;Android Firefox guide&lt;/a&gt; has the official extension listing and setup steps. This is a Firefox extension, not a NodeLane APK, and the store's compatibility checks still apply.&lt;/p&gt;

&lt;p&gt;Reading local originals does not require a NodeLane account. Official translation is optional, needs sign-in and an internet connection, and is subject to the current allowance or paid plan. A self-hosted translation service is another option.&lt;/p&gt;

&lt;p&gt;For feedback, I'm especially interested in three concrete things: which file format failed to import, which control was hard to reach, and whether opening settings or switching page versions moved the reading position.&lt;/p&gt;

&lt;p&gt;What desktop interaction did you have to rethink most when bringing an existing web app to touch screens?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI assisted the English drafting of this article. The implementation details were checked against the project code and documentation; unverified device and service behavior is called out above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>firefox</category>
    </item>
    <item>
      <title>I built an open-source manga reader + translator browser extension</title>
      <dc:creator>NodeLane</dc:creator>
      <pubDate>Sat, 26 Sep 2026 13:46:35 +0000</pubDate>
      <link>https://dev.to/_096282f0dc6decf9962b4c/i-built-an-open-source-manga-reader-translator-browser-extension-1bdm</link>
      <guid>https://dev.to/_096282f0dc6decf9962b4c/i-built-an-open-source-manga-reader-translator-browser-extension-1bdm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Android update — October 11, 2026:&lt;/strong&gt; NodeLane Comics can now be used in Firefox for Android. You can import your own comic files, read with touch controls, and switch between originals and translations. Start with the &lt;a href="https://comics.nodelane.net/en/guides/android-firefox/" rel="noopener noreferrer"&gt;Android Firefox installation and reading guide&lt;/a&gt; and the &lt;a href="https://addons.mozilla.org/firefox/addon/nodelane-comics/" rel="noopener noreferrer"&gt;Firefox Add-ons listing&lt;/a&gt;. This is an extension, not a standalone Android app; iOS / Orion compatibility is still in progress. Local original reading needs no NodeLane account. Official translation requires sign-in, connectivity, and an available allowance. The guide labels its emulator screenshots and their verification limits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey DEV! I’m building &lt;strong&gt;NodeLane Comics&lt;/strong&gt;, a manga reader and translator that runs as a browser extension.&lt;/p&gt;

&lt;p&gt;The idea is to bring manga from different places into one library: supported websites, local files, and Google Drive. Translation is optional — you can just use it as a reader.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjwsladxqpmvzycxvpcoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjwsladxqpmvzycxvpcoi.png" alt="NodeLane Comics library showing imported manga and reading progress." width="799" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep your manga in one place.&lt;/strong&gt; Import from supported websites, Google Drive, or local files such as CBZ, CBR, PDF, and DRM-free MOBI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep track of your reading.&lt;/strong&gt; Save your progress and check for new chapters from supported websites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate as you read.&lt;/strong&gt; Use a cloud translation service or connect a local manga-translator-ui instance, then switch between the original and translated pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate inside a browser tab.&lt;/strong&gt; Display translated manga images directly on supported pages without importing the comic into the reader.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also a cross-language title search to help you look for the same comic on selected websites in another language.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnt4i05dro26owfdyx7f8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnt4i05dro26owfdyx7f8.gif" alt="Opening a manga from the library, enabling translation, and switching between the original and translated pages." width="719" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A little about the build
&lt;/h2&gt;

&lt;p&gt;The extension uses TypeScript, React, and WXT, with a FastAPI backend. Both the frontend and backend code are public on GitHub.&lt;/p&gt;

&lt;p&gt;Local translation connects to a separately running &lt;a href="https://github.com/hgmzhn/manga-translator-ui" rel="noopener noreferrer"&gt;manga-translator-ui&lt;/a&gt; service — the extension itself doesn’t run the translation models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for feedback
&lt;/h2&gt;

&lt;p&gt;It’s still early. I’m working on supporting more websites and improving stability, especially around translation.&lt;/p&gt;

&lt;p&gt;Right now, I’m looking for people to try it with their usual reading setup and tell me what feels useful, confusing, or missing. Feedback on the reader itself is just as welcome as feedback on translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chromewebstore.google.com/detail/nodelane-comics-ai-manga/aiajdjliifeeaogpalejpggkiccjbneo" rel="noopener noreferrer"&gt;Try the Chrome extension&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Wy2926/node-comics" rel="noopener noreferrer"&gt;View the code on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you mostly read manga on websites, from local files, or a mix of both? I’d love to hear how you currently keep everything organized.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I used AI to help draft the English text for this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>comics</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
