kiwix / kiwix-js

Fully portable & lightweight ZIM reader in Javascript
https://www.kiwix.org/
GNU General Public License v3.0
302 stars 126 forks source link

Refactor program flow to maximize extraction efficiency from ZIM file #288

Closed Jaifroid closed 1 year ago

Jaifroid commented 7 years ago

149 (load images after stylesheets) doesn't encompass alone what I think is needed, and the thread has become too long. I think we need to look more closely at how promises are used and direct the extraction logically rather than "defer everything" (which causes a serious logjam in worst case scenarios). In https://github.com/kiwix/kiwix-js/issues/149#issuecomment-315642742 I suggest a flow, which I would refine like this:

  1. extract HTML synchronously (don't do anything else while the single thread is getting this, it's highest priority) ->

  2. If stylesheets are not cached, extract each stylesheet synchronously, ideally in the order they appear in the header, as they are needed for first paint to happen efficiently; allow spinner to advance between each stylesheet extraction ->

  3. defer the image extraction as a block via a promise (asynchronously), because there is no harm waiting for images and they are either all grouped together or grouped with the file; images will be extracted after step 4 below, but ensure images at the top of the page are extracted first (direct the order of extraction of images) ->

  4. Get on with the screen painting as fast as possible, so that image extraction can complete ->

  5. If javascripts are not cached, extract javascripts via a promise, so they can complete in the background, but does not compete with extraction of images.

NB Some stylesheets have little images in them, with ZIM file URL's in the "I" namespace: ideal would be to cache these (little icons) alongside the stylesheets so xdec doesn't have to touch them, but deal with them if not cached.

Jaifroid commented 7 years ago

Regarding performance, my first feeling was that it was much faster. I think it comes from the fact that I feel I can start reading the article because there will be no extra CSS applied. It's a clear usability improvement. But, if I compare the time to have the whole page rendered with master, the difference is not big. It's usually a bit faster on this branch.

@Mossroy , in reply to your comment at the end of #149 (load images after CSS), I now have some precise timings for the difference that preloading the CSS makes.

I have added to my Preload stylesheets branch a console timer that measures total time from click on article title to the receipt of the last image blob from xzdec.js. Combining this with the other console timers, we get timings for: receipt of html, extracting stylesheets, extracting images, and total time to document ready.

This proves that preloading the stylesheets has an independent effect on the amount of time needed to load the images. The headline figures are:

To render the Bolivia article from Spanish Wikipedia (which contains 6 stylesheets and 253 images) takes an unacceptable 63.2 seconds in Windows Mobile app, reduced to 28 seconds by preloading the stylesheets BUT first paint takes only 6.2 seconds (compared to 30 seconds without preloading the stylesheets), at which point, as you say, the user can happily start reading the article. Remember this is a two-year-old phone.

On Firefox on a PC, the total gain is from 6.5 seconds to 4.8 seconds, but time to first paint is reduced from 1054ms to 469ms. I know a user would hardly notice that, but it's actually 124% gain.

I think with @sharun-s 's code, especially loading images above the fold first, the two improvements together can make a stunning difference. I am posting shots of the results in the next three comments.

Jaifroid commented 7 years ago

image

Source spreadsheet

Jaifroid commented 7 years ago

image

Source spreadsheet

Jaifroid commented 7 years ago

image

Source spreadsheet

Jaifroid commented 7 years ago

@sharun-s , @mossroy I have now added code to extract the images in batches which makes a big subjective difference to the user experience. The images are extracted starting at the top in slice sizes that can be set at line 987. This is in the preload-stylesheets branch.

While so far, with an image slice size of 5 images, the overall time to document ready is not improved, what happens is the page seems to the user to load very quickly with the first five images ready. The rest are pulled in the background. The subjective experience is much improved on slow devices. On Firefox on a desktop, I think the difference is minimal, but for anything mobile, so far the difference is impressive.

I obviously want to experiment with different batch sizes, or perhaps with doing the first five or so and then all the rest, to find the optimal overall experience. I hope you get a chance to try out this branch at the weekend, especially on slower devices or browsers. There's extensive (too much) info in the console log, but it's fun to see the background image-loading in action after the page has rendered.

I'm sure the code can be improved (it's in a similar direction, but a "light" version, of @sharun-s 's innovations), and some further debugging needs to happen, but it's looking very usable.

sharun-s commented 7 years ago

@Jaifroid I am slightly preoccupied with workers and the issues they have generated to test your stuff this weekend, but here are a couple things to mull over as you experiment with image loading...maybe you can avoid the mess my code is in by thinking about this stuff earlier than I did.

  1. Code reusability - Think about how you can adapt your approach to support search, specifically something like the ios version of search results You can see it requires the reading of the first image from multiple articles. Which is basically reusing a lot of the same code that does an article read/images load. This entire approach would also again be reusable in something like image search. I ended with 3-4 tangled up copies of the same functions just to support the minor variations

  2. "Viewable" area.
    Users jump around from link to link fast. So if you start at Paris and jump to France and then to Denmark without scrolling at all, there is a bunch of loading/battery drainage that can be avoided. A batch loading approach tied to viewable area would address this. I just haven't found any time to research how to compute something like that. I see it being used by a lot of sites (youtube/instagram etc) to do progressive image loading.

I am itching to lay down a draft design + implementation specification - maybe on a wiki page. To layout/workout in one place current api/behavior & desired api/behavior, and how to keep the code modular(reusable), performant and easily extensible for anyone new who wants to jump in. Will try to put some rough thoughts down sometime during the week.

Jaifroid commented 7 years ago

OK, thanks for the tips, @sharun-s .

The latest commit, just now, includes the possibility of setting a "firstSliceSize" variable, and a "sliceSize" variable. I have worked out that by setting firstSliceSize to 10, we more or less guarantee to load all images visible to the viewer on first page load, and then subsequent sliceSize can be set to 200 or any arbitrary large number, to allow all the rest to load in the background with the greatest efficiency. This provides a perfect balance between subjective speed and efficiency of background extraction.

Regarding No. 2 (Viewable area), my approach to this so far has been the pragmatic one outlined above of loading 10 images. Actually 6 will often do it, but extracting 6 or extracting 10 makes very little difference. I think what you say could be achieved properly by halting program flow after loading the first 10 images or so, and resuming on a scroll event. That way, if the user clicks on Paris, then jumps straight to France, you don't needlessly extract all 280 "Paris" images. If anyone knows how to determine which images are in the visible area at any one time, that would be a huge help.

Regarding No. 1 (code reusability), all I've done is added some code to slice up the image array into batches, and I'm directing the program flow by making the fulfilled promise callback itself call the next slice of images. That way, the main program execution comes to a halt while xzdec.js is extracting the images, and gets called again when xzdec has finished extracting the images. Other than this, the image extraction code is still the same code written by @mossroy et al.

The iOS app search is merely a version of the mobile Wikipedia style search. I doubt the iOS app is compiling those pretty search images. You can see very similar in mobile mode on the Web site, e.g. here:

image

I think it would complicate our app too much to try to imitate this, and it would be a battery guzzler... But, you never know, your Web Workers are magic...

Jaifroid commented 7 years ago

@sharun-s , the Lazy Load jQuery plugin might do the trick, but it's yet another library and jQuery is already causing a bottleneck in our code. For example, slicing a jQuery document object containing 280 image objects (Paris page) -- without the image data loaded into the object yet -- takes almost as long as loading all 280 images! I feel another Regex coming to the rescue...

Jaifroid commented 7 years ago

OK @sharun-s and @mossroy , commit https://github.com/kiwix/kiwix-js/commit/89e8c34059f55cea885bc3a7a9689b599103a6d1 (the Load images on scroll branch) contains code that loads the first 10 images (number can be set to higher or lower) and then the rest when the user first scrolls the iframe window.

Unfortunately, I don't think this is usable, at least on mobile (which is where it's needed). If you run the code even in a browser, you'll see a) there is a noticeable delay between the event firing and the arrival of images; and b) because of the promises used to extract the images, nothing happens while you are still scrolling (you can see in the console.log that the event has fired, but the promises are sitting around waiting for the user to stop scrolling before xzdec even starts up).

I'm a bit annoyed because although the code looks simple, it took several hours to get it working, and no matter how or at what point in the code I set up the onscroll function, I kept hitting this infuriating delay... Frankly, on mobile, the delay is unacceptable. You scroll half-way down a long article and all the images are missing. Then, as long as you don't keep scrolling, they very leisurely start to turn up.

I suspect what is happening is that there's a high overhead in terms of initializing arrays and memory for xzdec to start up. Either that, or the promises are just letting other things happen first. If either of you two experts could look over the few lines of code and see if you think I've made some noob error that's hitting performance, let me know. Otherwise, I think I have to chalk this up to a failed line of experimentation. Frankly, it would be better just to have a 5 second delay or something. Or maybe it's just best to get on with the image loading in the background and to hell with battery life....... Any other ideas welcome.

sharun-s commented 7 years ago

Good experiment. No experiment is a bad experiment! Validates why speedup happens when some of that workload (namely all those gerDirEntrybyTitles) are pushed off the main threads job queue onto a workers jobqueue.

Why is slicesize set to 200. I would think dropping this to whatever the "img count within viewport" should make things a bit better. Or just set slizesize to 2-5 and see what happens. This would drastically reduce the number of lookups that happen. Lookup time(binary search) is directly proportional to the article count of your zim (if not using a direntcache).

time estimate for lookups = no.of.images 2 binarysearchsteps per image. So 200 will produce a huge number.

[where binaryserchsteps per image = log2(article count)]

sharun-s commented 7 years ago

I can see this code being reused once worker code is done.

Jaifroid commented 7 years ago

Why is slicesize set to 200. I would think dropping this to whatever the "img count within viewport" should make things a bit better. Or just set slizesize to 2-5 and see what happens. This would drastically reduce the number of lookups that happen. Lookup time(binary search) is directly proportional to the article count of your zim (if not using a direntcache).

@sharun-s , thanks for encouragement and tips. sliceSize is the second sliceSize (the first one is set to 10), and it is set in the code to 200 because some articles have over 200 images. Don't worry, though, because this line sets it to the actual image length. It does not do 200 lookups if there are only 16 images! It will do 10 (first slice) + 6 (second slice). However, if there are 289 images (the case of the "Paris" article in English Wikipedia), then it will do 10 + 289 -- this is because it adds the modulus / remainder onto the end of the last batch if the subsequent batch would be less than a complete full batch.

I experimented with different lookup slices. The smaller the slice, the longer the process takes to complete. The result of my experiments is that the optimal balance between user experience of fast page load and speed of completion of background loading is to do 10 images initially, display them, and then, while the user starts reading, load all the rest in the background as a single batch. There is no logjam when it is just images loading, as they are all in the same namespace /I/ of the ZIM file, so there is no seeking backwards and forwards across the file, and it's best just to let xzdec get on with it.

Jaifroid commented 7 years ago

@sharun-s YOU WERE RIGHT!!! Setting the second sliceSize to a smaller number does make a big difference in the context of the scrolling code! Your mentioning of the lookup time required got me thinking that directory lookups were in fact the logjam in the case of the scrolling code. So, basically, you're absolutely right that if I ask it to look up the remaining 271 images in one go then the delay is caused not by the promises sitting around waiting for other stuff to happen, but by the fact that it goes off looking up all 271 images in the directory first before actually retrieving any of them.

So, setting the second slice to 20 means that as you scroll down the article it keeps loading the images 20 at a time, on each scroll, until it gets to the end. This is much better and much faster for the user.

Thank you, thank you for your insight, made it all worthwhile! (Just pinging @Mossroy too to let him know.) I think we're getting somewhere now, and if we can incorporate your Web Worker code, @sharun-s , we've got some powerful performance tools now.

Commit https://github.com/kiwix/kiwix-js/commit/f1b2a3b82edcd1df06f292acca6e66369f31826e now has a second sliceSize of 20 and I've made the comments more readable. When either of you get a chance to try it out (desktop browser for now, this hasn't been tweaked for FFOS), you'll clearly see in the console log how the scrolling and the loading interact.

sharun-s commented 7 years ago

Good feeling init, once that pesky piece of code finally works :-)

Jaifroid commented 7 years ago

Thanks, @sharun-s :-) Just a quick note to say I've merged the changes from the "Load images on scroll" branch into "Preload stylesheets", and as of this moment both branches are the same. Future changes will be to "Preload stylesheets".

Jaifroid commented 7 years ago

Hey @mossroy and @sharun-s , last week I discovered a nice little function that easily allows you to determine the elements that are visible to the user in the viewport. I incorporated it into my code, but the code turned into a quivering pile of spaghetti :-) .... So, I went back to the drawing board, did a flowchart, and refactored the image triage to do it "properly", with the knowledge I have now about how to do it. This is a major upgrade, as of commit https://github.com/kiwix/kiwix-js/commit/772488b522615241f97a206ddc3295bc8b7f6be5 , and I've fully documented each function now with the js.doc style.

The flow is this: when we get to first paint, scan through all images and sort them into three buckets or slices:

  1. Visible images (visibleSlice);
  2. Offscreen normal images (i.e. not svg) that we want to prefetch (prefetchSlice);
  3. SVG images, which are s-l-o-w (svgSlice).

The program then loads the images in that order, starting with ALL visible images, and "prefetching" a selectable number of offscreen images, either from below the current viewport, or, if we are at the end of the document or have fetched all images below, from above the current viewport. It then deals with svg images within the prefetch routine in a specialized way, loading them in mini batches of between 1 and 5 (developer-selectable) at a time, to prevent the system from hanging. After having prefetched the required number of images and/or SVGs, the program stops and waits for the user to scroll (technically it waits for a poly-filled scrollstop event), It then repeats, starting with any visible images not already fetched. If the user scrolls during the (slow) extraction, the program detects this and starts loading visible images again when the scroll stops.

This SVG loading mechanism copes with Sine.html very nicely in Firefox. Edge struggles to keep up but it doesn't hang :-) . Chrome struggles quite badly with Sine.html and sometimes hangs -- it seems to need to keep the mini-batches at or below 3. But this document is an extreme, edge case.

Pages with normal images really fly with this approach on Firefox, Edge, UWP, Internet Explorer and FFOS. Try loading up Wikivoyage.fr in FFOS simulator, go to the Paris article, click somewhere in the window (not on a link) and press Ctrl-End on the PC keyboard. The article will scroll to the end, and immediately start loading the mass of little flags at that spot. Below is a screenshot. Please have a play and let me know of any issues you notice.

image

sharun-s commented 7 years ago

Nice work @Jaifroid :-) Lot's of very useful building blocks here (scroll handling, typechecking, elementinview etc) that will solve other problems too. An efficient loading framework is taking shape. Put down your drawing board/flowchart stuff once it is looking clear on paper, get comments on it, then code. You will get more bang for your buck. I should too! But the rule doesn't apply if you are totally enjoying yourself, which I can see you are :-)

This is a minor point - the two scrollbar issue mentioned in #267, how does it play into this scroll handling. Does it matter?

Now to bigger things - I am trying to keep the worker interface as simple as possible so that it can be used with minimal modifications for different loading strategies. And I want your thoughts on how your batch mode code can fit in.

As of now I have created a module called finder (temp name that can be changed later) It's job is to handle:

  1. Worker creation Determine number of workers to create - tied to no. of urls AND tied to the loading process - load all, load above the fold, load in batch etc
  2. Worker scheduling When does each worker get started? If it is has to wait on something, how is waiting controlled?
  3. Distribution of urls to each worker How the url list gets split up - even split, above the fold + remaining even split, nosplit
  4. Result handling Notify UI when results are ready AND on worker completion events (first worker completed, N workerscompleted, all workers completed)

A separate module allows as much of the above crap as possible, to be moved off app.js.

Note there are lot of potential algos when you vary the above 4 pieces. But the idea is whatever the combo it can be encapsulated as a "strategy". And all strategies can be available to whoever wants to use them/as the situation requires.

On app.js it would result in one call - find(Urllist, strategy, resultCallbacks) resultCallbacks are onEachResult, OnFirstWorkerCompletion, OnAllWorkerCompletion etc and will contain code only UI related like inject image into dom, update progress bar etc

You can see it in action in two places - displayArticleInFrame and image search

Now the question is, how your progressive load code can fit into this interface? Or if modification are required. I'll be thinking about it as I look at your code sometime this week and it will help that you are thinking about it in parallel.

Jaifroid commented 7 years ago

Hi, @sharun-s , thanks for the detailed thoughts and for thinking about how to integrate the code sets. I'll respond to different issues in different replies, as some need thinking about. Just quickly on this point:

This is a minor point - the two scrollbar issue mentioned in #267, how does it play into this scroll handling. Does it matter?

The onscroll and stopscroll events are only triggered on scroll of the article contents, from my quick testing of this. Even if scroll of the navbar were to trigger the event, the only thing that would happen would be, potentially, that more images might be prefetched in the background. So, basically, this code just listens for the iframe scroll but doesn't otherwise alter scrolling in any way.

Jaifroid commented 7 years ago

@sharun-s , quick thought on worker scheduling: the main reason I'm having to "direct" things from within app.js is because otherwise we get a logjam, but also to prioritize the loading of visible images. The original code seemed to load images from the bottom up, or at least just loaded whichever image happened to complete its promise first, and that often meant the main image at the top of the page was loaded last in heavy multi-image pages.

If your workers can be invoked to undertake discrete jobs, and return with either a blob URL or the data contents (ideally either, rather than hard-coding blob creation in, or just return the data and the blob can be created with the utility for that), then it would be really easy to integrate into my code, probably here. It would be good if your workers could also handle css, so they could be invoked here, or possibly a little higher up, with an array of CSS links to extract.

Basically, the easiest to integrate would be if your worker subroutine could handle an array of images sent to it (a node generated by a standard getElementsBy... function, but also a jQuery node) and return the array with the src attribute filled, either with a blob URL or, on request, a data:image/jpeg;base64,"data" string, as I request here (note you have to do a util.uintToString(data)) before doing btoa on it, like this). If it's a jQuery node or a getElementsBy... node, then filling the SRC will automatically update the document and display the image.

Alternatively, it could handle things at a lower level: accept an array of ZIMfile URLs (not a node) and return the array with an object mapping each URL to either a blob URL or to a string/Uint8 arrray containing the raw contents.

If you're doing the former (work on a node object), then it would be great if your code could also be requested just to supply the raw data if they are needed.

Alternatively, your routine could also handle the sorting and prioritizing logic for the images, basically what my routine does at a higher level. However, I've already written this logic, so there's no need to reinvent the wheel unless it can be improved. The logic is quite simple: prioritize visible images, prefetch x offscreen images, deal with svg separately because they're resource hogs.

There's one more consideration that occurs to me: my code is part of the long if (contentInjectionMode === 'jquery')... clause that begins here, i.e., it doesn't touch serviceWorker mode. Does your code aim to replace serviceWorker, jQuery, or both methods? And there may be clients on which Web Workers don't work, so we may need to keep the single-threaded mode.

sharun-s commented 7 years ago

@Jaifroid The idea behind the worker stuff is to abstract and encapsulate only "lookup" operations. Purely the binary search steps that takes a url and returns a dirent. So URL is input DirEntry is output. That extends to list of URLs as input and list of DirEntries as output.

Reading blobs, dom manipulations etc I treat as separate operations. And there are some benefits to keeping them separate.

I can change input and output to the worker, to all kinds of things like DOM nodes, jquery nodes, blobs etc but that would specialize the code. These "other" inputs and outputs are specific to higher level operations like article load or image search or keyword search etc and not specific to lower level lookup.

Other kinds of inputs and their transformation to urls if handled separate from the lookup worker allow lookup code to be reusable, no matter what process generates the input url list. That url list can be produced from a list of dom nodes/list of direntries/json obj/textfile etc. (or even in the case you suggested being able to lookup regex generated Rembrandt url patterns).

The code that makes the call to the worker is then responsible to map the dirent results returned, back to whatever produced the url list if that makes sense.

The reason for all this is really a missing piece in the design. The app has a concept of a zimArchive, zimFile, DirEntry, decompressor etc. But is missing the concept of an Index. Specifically a TitleIndex and a URLIndex module. And in future, if we plan to support the kind of stuff the Wikipedia Search bar supports, then all kinds of other complex indexes. Which are basically lookup mappings + the process to do them efficiently.

Because this Index piece is missing the lookup process currently finds itself getting repeated in all kinds of unnecessary places.

So to answer your question content injection mode whether it is based on "parsing a doc and injecting assets"(jquery mode) or "intercepting asset requests and injecting assets"(service worker mode) will both need lookup underneath. The only reason I haven't touched serviceworker mode is I haven't found any need for it so far. I am mostly interested in "standalone" mode where you click the index.html and you are good to go. And in this mode service worker doesn't work in any browser. I haven't decided what to do with it mostly from lack of time. It clutters up app.js and I have half a mind to remove it from my branch or move it in to a separate module. But I am keeping it around as the request interception logic might be reusable elsewhere.

Hope the long story makes sense, and feel free to point out areas of confusion as writing this stuff down makes me clarify things to myself too!

Jaifroid commented 7 years ago

@sharun-s This sounds great. I think a fast, multi-worker lookup function like this is precisely what's needed. The dirEntry lookup seems to be extraordinarily intensive for some files and not so for others. So, to be clear, what is returned is the Directory Entry object, which includes things like physical location pointers in the ZIM file, a readable title if there is one, etc. We then do a Read Binary File on that directory entry. Are you sure Read Binary File wouldn't benefit also from being hived off to workers? But if what you're producing is a drop-in, multi-worker replacement for getDirEntryByTitle, then it would seem very easy to integrate into existing code.

sharun-s commented 7 years ago

We then do a Read Binary File on that directory entry

Exactly

Are you sure Read Binary File wouldn't benefit also from being hived off to workers?

Two reasons not to do it right now-

  1. This would change the calling interface from URL->DirEntry to URL->Blob. In not all lookup cases do we need a blob, but in all lookup ops we do need a direntry returned. As covered above, it would break where the call can be reused. Just keeping it simple for now.
  2. Would there be a benefit to do readblobs in workers - probably - honest answer I don't know - out of pure luck the worker doesn't need to do any decompression operations right now, so things are simple. Pulling that unpredictable bastard xzdec into the worker might open a new can of worms ...or it might not. Right now performance is looking good and it's only worth going there, if some new problem crops up.

drop-in, multi-worker replacement for getDirEntryByTitle

That is exactly what it is with the addendum that getDirEntryByTitle is actually getDirEntryByURL. If you look at the parameter being passed in and operated on everywhere they are all URLs and not Titles. It's very misleading, confusing and leads to subtle bugs here and there. I have made some changes to address it here

It's the difference between looking up Paris.html or Paris.png Versus looking up Paris. The only place where a Title to DirEntry lookup is required is in search. It's not required in article load or image loading at all. I had to do a lot of renaming all over the place to differentiate the two.

mossroy commented 7 years ago

I'm impressed by the amount of research and code you're both doing. Congratulations for this work, even if it's hard for me to follow everything!

What you did on your branches shows that we can achieve major performance improvements in jQuery mode. Which is great news because it is much needed. But it will also take some time and discussions to do the triage and cleaning of all these optimizations before merging (some of) them. Keep in mind how much time it took you to understand the logic of the code : we must find a way to keep the code simple so that it's maintainable. Even if it means not merging all the optimizations.

I agree with @sharun-s about the need to have the different layers as separate as we can. I think the current code mostly respects this design pattern : we need to keep that.

Regarding the different injection modes, here are my thoughts : The ServiceWorker mode is certainly the best technology fit for our need. Because it is "naturally" multi-threaded, because it traps all the requests (and not only the ones we manage to catch with jQuery), because it probably benefits from all the built-in optimizations of a browser (fetching the visible images first, maybe putting them in its cache etc). Unfortunately, ServiceWorkers are not (yet) available in most of the currently-supported platforms. So we have no other choice to maintain (and optimize) the jQuery mode for now.

It's highly probable that some of the optimizations you're working on will be useless in ServiceWorker mode. So we'll probably want to keep them apart at some point, for compatibility with older platforms, while we concentrate on newer platforms. But I'm talking about the future, it's too early for now.

Regarding WebWorkers, I think we can assume that all our platforms support them. See http://caniuse.com/#feat=webworkers

What would you think about discussing about kiwix-js within a videoconference, one of these days? I have the (rare) opportunity to take some time for that next weekend (Saturday 5 or Sunday 6), or Wednesday 9, or even in some evenings of next week.

sharun-s commented 7 years ago

Good idea @mossroy the weekend is fine with me. You guys can pick a time. I am in India so 4-5 hours ahead probably.

Jaifroid commented 7 years ago

@mossroy , @sharun-s , I'm away most of the weekend, but could manage most afternoons/early evenings next week up to 6.45pm British Summer Time == 5.45pm UTC. Wednesday 9th would work well for me, can be earlier in the day too and probably should be if @sharun-s is 4-5 hours ahead.. We should probably make precise arrangements by email off this public board.

I agree with your points, @mossroy , and never expected kiwix-js to adopt everything I've been doing. Whatever is seen as most useful, really. I think the image triage system is the easiest to adopt, as it's self-contained in a single function with sub-functions, and I've commented them all with JSDoc standard, full explanations and params. Can also be easily separated from the stylesheet caching (not dependent on each other).

I still haven't tested ServiceWorker mode, but I know it doesn't work with UWP yet, and this is my main target, so I've been focusing on "jQuery" mode (which is a bit of a misnomer...).

I want to get at least a Beta of kiwix-js-windows that is acceptable into the Windows Store by middle of month, and one that isn't going to get slated with comments like "It doesn't do anything", or "It's really slow and crashed", or "Unusable because no dark mode" (this is a very common complaint amongst Windows Mobile users because they mostly have AMOLED screens, so anything with blazing white is a battery guzzler). I want to start with one plain Kiwix JS UWP, with clear instructions on how to download and pick a ZIM, and one with Wikivoyage English to start with. I'm nearly there. Just need to use the UWP API for filepicking and remembering the folder picked, which is the last piece of the puzzle to make it useable. Oh, and maybe better functionality on switching between tabs, because as you know I coded a fudge that wasn't great.

Jaifroid commented 1 year ago

A lot of this has been achieved.