If Tab Stash has improved your life, and you want to leave me a one-time tip, or even a monthly sponsorship, I would be very grateful. Tab Stash will always be 100% free and open source, no data collection and no strings attached.
First of all, thanks for your interest in making Tab Stash better! Tab Stash has been a labor of love since 2018, and I never would have expected that N years later [what year is it again?], it would have grown to where it is today.
Below, I've included some information and resources to help you modify Tab Stash and submit your change for inclusion in future releases. You'll have an easier time of it if you already know some JavaScript, but even for those who don't, I've included a few references to help you get started.
If you plan to make a substantial change or add a new feature, it's best to start by discussing it on a GitHub issue before you write any code. This helps to ensure it fits into the overall vision/direction for Tab Stash, and helps to identify potential issues or roadblocks ahead of time.
Once your change is ready, you'll need to push it to a branch on GitHub and open a "Pull Request". To maximize the chances of your PR getting merged, there are a few things you should do before submitting:
-
Include some automated tests verifying your changes behave as expected (if applicable).
-
Make sure your PR follows the style and other conventions listed below.
-
Do a "self-review"---read through your own changes as if you were a code reviewer, clean up any unnecessary changes (e.g. whitespace-only changes), fix any typos, add comments/documentation, etc.
-
Write a detailed/clear summary and description of your PR explaining what your change does and why. If you're addressing any GitHub issues, be sure to reference them by number in the summary and/or description. Typically you would put the issue number in [brackets] at the end of your summary.
Importantly, don't expect your PR to be merged right away. You will likely get at least one round of constructive feedback; this is to help catch bugs and ensure the code stays maintainable for the next person who wants to contribute. I hope you will take this feedback in the spirit in which it's given---as reflecting our shared desire to make Tab Stash the best it can possibly be.
Again, thank you for your interest in contributing!
--- Josh
Before you get started, you may want to spend some time familiarizing yourself with the tools and technologies Tab Stash is built on. Here are some things that would be useful to know (or at least references to have handy) when you dive into the code:
-
If this is your first time coding, here's where to start:
-
Learn about the languages used in Tab Stash:
- JavaScript
- TypeScript
- HTML for building web pages, and
- CSS for making them look pretty
-
Learn how extensions for Firefox are put together.
-
Learn about the major libraries and frameworks used in Tab Stash:
Here's how to get a build with your changes loaded into Firefox so you can try them out:
-
Clone Tab Stash's source code from GitHub.
-
Follow the instructions in the README to build Tab Stash for development. You should see that all the tests are passing.
-
Load your build into Firefox:
-
Go to
about:debugging
and click on "This Firefox". -
Click "Load Temporary Addon..." and choose the
manifest.json
file in Tab Stash'sdist
directory. -
The Tab Stash sidebar and toolbar button should appear.
-
-
Make your changes:
-
Use your favorite editor (e.g. Visual Studio Code) to make your changes.
-
Rebuild Tab Stash and run the unit tests (just run
make
). Be sure the tests pass before proceeding. -
Use
about:debugging
to reload the extension, and try out your changes. You can also useabout:debugging
to inspect and debug the various components of Tab Stash (background page, UI pages, etc.). -
Repeat until you're satisfied with your changes.
-
-
Once you're happy with your changes, push them to a branch on GitHub, and open a Pull Request. (See the introduction for advice on how to submit a good PR.)
-
I'll review your change and work with you to address any issues. Then, if/when everything looks good, I'll merge it and it will become part of the next Tab Stash release!
During the build, a few different types of source files are combined to produce the final Tab Stash extension. Let's go on a quick tour:
-
Files in
assets/
are copied directly into the extension unchanged.manifest.json
describes the extension to the browser. This is the place to start to understand how the browser interacts with Tab Stash.
-
icons/
contains the SVG icons used throughout the Tab Stash UI. Icons are always expected to be monochromatic, and will be re-colored during the build to work with both light and dark themes. Some icons are converted to PNG images where required by the browser. -
styles/
use CSS (as processed by Less) to define how the UI looks. In general, styles are broken down as follows:-
index.less
is the "top-level" file which loads all the others and acts as a "catch-all" for styles that don't fit anywhere else. -
themes/*.less
files define colors as CSS variables used throughout the styling and source code. (In general, this is the ONLY place where colors should be defined.) -
metrics/*.less
files define measurements as CSS variables used throughout the styling and source code. (In general, this is the ONLY place where lengths/measurements should be defined.) -
All the other
*.less
files describe how to lay out various parts of the UI, referring to the colors and lengths/measurements defined in thethemes
andmetrics
files.
-
-
src/
is where all the action is---all the TypeScript and Vue.js code that makes up Tab Stash lives here. Here are some places to check out to learn your way around:-
src/index.ts
is the main entry point for the background page (the part of Tab Stash that is always loaded in the background). Integrations with the browser (e.g. the context menu, toolbar button, etc.) are all defined here, along with several background tasks Tab Stash needs to perform to keep things running smoothly. -
src/stash-list/index.ts
is the main entry point for the Tab Stash UI. (The same UI is used for all views---sidebar, full page, and popup.) The UI itself is defined in the correspondingsrc/stash-list/index.vue
file. -
Similarly, there are entry points for the options page, deleted-items page, etc. in the
*.html
files in the top level ofsrc/
. You can find a list of such entry points in the top-level filesvite.config.html.ts
. All of the HTML pages follow a similar structure to the stash list---src/foo.html
includessrc/foo/index.ts
, which bootstraps the page and loads the top-level Vue component, which issrc/foo/index.vue
. -
Finally, it's worth checking out
src/globals-ui.ts
andsrc/service-model.ts
.globals-ui
constructs the global "model" data structure for the UI, andservice-model
does the same for the background page. These two files together give an "architectural blueprint" for how Tab Stash is organized and how it tracks all the data it needs to do its job.Each of these files refer to various "models" (which live in
src/model/
) that track and modify specific things, such as open tabs, bookmarks, deleted items, Tab Stash options, etc. The various models are all used by a "root" model (src/model/index.ts
) which defines the core behaviors of Tab Stash (e.g. stashing and unstashing tabs).
-
There is a lot more to the codebase, but hopefully this is enough to get you oriented and keep you from getting lost. Most of the rest should be easy to find by reading code and poking around. And if you do get lost, feel free to ask a question on GitHub!
There are a few global variables made accessible to you from the console so you can inspect the state of the application at runtime:
-
app
: The Vue application -
the.model
: The root model (seesrc/ui-model.ts
for the UI's root, orsrc/service-model.ts
for the background page's root). (There is alsothe.version
. Eventually, all stateful globals are expected to move or be aliased underthe
, so there is only one true global, for easier discoverability.) -
error_log
: The error log used byoops
to collect and report errors to the user. -
trace
: This is an object with boolean values to enable debug logging for various parts of Tab Stash. Note that some of these logs might get VERY verbose and/or cause performance problems, so be careful about turning on too many of them for too long.
Note: The existing code does not always follow these guidelines consistently; if you find inconsistencies, please feel free to correct them (but please submit corrections in PRs which are separate from functional changes).
Try to use clear and descriptive names, but use your best judgment---the larger the scope, the more carefully you should think about the name. A function that is used everywhere in the code should have a very clear and descriptive (but not necessarily long!) name. By contrast, single-letter variable names are fine if the contents/usage of the variable are obvious in context and the variable's scope fits on a single (small) screen.
-
Named Constants are written
LIKE_THIS
. -
Class Names are written
LikeThis
. -
Exported/Public Names are written
likeThis
. (This applies to functions, methods, properties, arguments, global mutable variables.) -
Private Member and Local Variable Names are written
like_this
. Private members may also have a leading underscore (_like_this
) to avoid confusion. Preferconst
for local variables when possible, orlet
when necessary. Don't usevar
.
-
API Docs: JSDoc-style comments (
/** ... */
) should be written for exported classes/functions. It is not necessary to write formal parameter/return-value/exception documentation unless it helps with clarity.Documentation should focus on behavior, not implementation---save any discussion of the implementation for comments inside the function body. What are the observable effects of calling the function (or using the class), assuming it is a "black box"? Make sure to note behaviors under edge cases, behaviors in the event of a failure, and similar details.
Avoid discussing the implementation, and especially avoid re-stating the purpose of the function/variable/argument/etc, which should be obvious from its name. (The worst documentation is something like:
@param timeout The timeout in milliseconds.
Well, duh. Instead, name the parameter something liketimeoutMS
, skip the documentation entirely, and save everyone some space and time.) -
Comments: Comments are encouraged in function bodies, and to provide general overview/design notes in modules, classes, etc. As with API documentation, avoid re-stating what the code is doing. Instead, focus on documenting your intention---explain why the code is written the way it is, and explicitly state your expectations and assumptions. Use
//
comments, not/* */
comments.
Tab Stash uses Prettier to format everything. Yes, sometimes it makes weird decisions that look strange or waste a lot of space, but it also removes a lot of the tedium in making sure your code is formatted consistently. Prettier runs as part of the build, and it will automatically re-format your code to meet its standards. If you use Visual Studio Code as your editor, you can install the Prettier extension and Tab Stash's default project settings will make sure you rarely have to think about formatting.
Inkscape is the recommended tool. Please be sure to follow the Firefox Photon Design Guide.
As noted above, icons must be monochromatic or the post-processing done to
convert icons for light/dark themes will not work well. The post-processing is
very dumb (it's literally just a sed
command); only fill colors should be used
(no line colors), or the finished result will look weird. If in doubt, follow
the conventions in the existing SVG files.