Giter Site home page Giter Site logo

pf2etoolsorg / pf2etools Goto Github PK

View Code? Open in Web Editor NEW
86.0 3.0 56.0 318.72 MB

A site dedicated to making playing games with your friends as easy as possible.

Home Page: https://pf2etools.com/

License: MIT License

HTML 10.11% JavaScript 75.47% Shell 0.12% SCSS 5.23% Python 0.40% Dockerfile 0.01% CSS 8.68%
pf2e website pathfinder-2e pathfinder-second-edition pathfinder2e pathfinder-rpg

pf2etools's Introduction

Pf2e.Tools

pages-build-deployment Build and Deploy

Visit the main site (not yet) or go to the unofficial GitHub mirror.

Join the Pf2eTools Discord here!

Help and Support

Please see our Discord server for FAQs, installation guides, supported integrations, and more.


Developer Notes

Data Sources and Versioning

Only "official" (that is, published by Paizo) data is to be included in the site. Anything else should be added to the homebrew repository.

Prioritise RAW above all else. Aim to provide a 1:1 copy of the original data. Obvious typos (for instance, mathematical errors in creature statblocks) may be corrected at the discretion of the maintainer(s).

Aim to use the latest version of any published material. Older versions which are sufficiently different (and relevant to community interests) can be moved to the homebrew repository.

Target JavaScript Version

Targeting ES6 was the original intent, but more modern features have long since crept into the code. Therefore, if something is available as standard in both Chrome and Firefox (preferably in versions dating back at least a couple of months), and isn't bleeding-edge, one can reasonable justify using it. As with all things, use common sense.

Style Guidelines

Code

  • Use tabs over spaces.

CSS

  • The BEM ("Block Element Modifier") naming strategy should be used where possible.

Data/Text

  • Format JSON to match the default output of JavaScript's JSON.stringify (using tabs for indentation), i.e. one line per bracket and one line per value. JSON files programmatically generated from other JSON files (i.e. those stored in data/generated) should be minified, however.

  • When "tagging" references in data (e.g. {@creature goblin}), the following rules apply:

    • Only tag references which are intended as references. For example, the Wizard class in You gain one cantrip of your choice from the wizard spell list should be tagged, whereas the Wizard class in Together, a group of seven powerful wizards sought to contain the demon should not be tagged. One is a reference to the mechanical class, one is merely the casual usage of the word "wizard."
    • In a similar vein, never tag anything within a quote-type block. Even if the quote directly refers to a specific creature, we can assume the quote is from a universe/perspective in which (for example) statblocks don't exist, and therefore the tag should be omitted to maintain the flavour of the quote.
    • Within data from a source, avoid referencing content from a source printed after the publication of that source.

JSON Cleaning

Parts of the JSON cleaning & style guidelines are also automated and can be applied using npm run clean-jsons. Additionally, this cleanup script is also run automatically as part of npm run build.

Trailing commas

To remove trailing commas in JSON:

Find: (.*?)(,)(:?\s*]|\s*})

Replace: $1$3

Character replacement

  • should be replaced with '
  • and should be replaced with "
  • (em dash) should be replaced with \u2014 (Unicode for em dash)
  • should be replaced with \u2013 (Unicode for en dash)
  • should be replaced with \u2212 (Unicode for minus sign)
  • should be not be used unless the JSON in question is not yet covered by the entryRenderer, i.e. should be encoded as a list
  • the only Unicode escape sequences allowed are \u2014, \u2013, and \u2212; all other characters (unless noted above) should be stored as-is

Convention for dashes

  • - (hyphen) should only be used to hyphenate words, e.g. 60-foot and 18th-level
  • \u2014 should be used for parenthetical dash pairs, or for marking empty table rows.
  • \u2013 should be used for joining numerical ranges, e.g. 1-5 should become 1\u20135.
  • \u2212 should be used for unary minus signs, in the case of penalties. For example, "You have a -5 penalty to..." should become "You have a \u22125 penalty to...".
  • any whitespace on any side of a \u2014 should be removed

Convention for measurement

  • Adjectives: a hyphen and the full name of the unit of measure should be used, e.g. dragon exhales acid in a 60-foot line
  • Nouns: a space and the short name of the unit of measure (including the trailing period) should be used, e.g. blindsight 60 ft., darkvision 120 ft.
  • Time: a slash, /, with no spaces on either side followed by the capitalised unit of time, e.g. 2/Turn, 3/Day

Convention for Dice

Dice should be written as [X]dY[ <+|-|×> Z], i.e. with a space between dice and operator, and a space between operator and modifier. Some examples of acceptable formatting are: d6, 2d6, or 2d6 + 1.

Convention for Item Names

Item names should be title-case, with the exception of units in parentheses, which should be sentence-case. Items who's volume or amount is specified by container (e.g. (vial)) treat the container as a unit.

Mouse/Keyboard Events

Avoid binding ALT-modified events, as these are not available under MacOS or various Linux flavors. Binding SHIFT-/CTRL-modified events is preferred.

Dev Server

Make sure you have everything installed (npm install) and built (npm run build) to have a fully functional site. Run npm run serve:dev to launch a local dev server that serves the project files on http://localhost:8080/index.html.

JSON Schema

The repository contains a JSON Schema for the data files in test/schema-template/schema.json. The schema is currently a work in progress.

Details for how to make use of the schema vary based on what setup you are using to work with the repo.

Visual Studio Code

To use the JSON Schema with Visual Studio Code, head to Settings and locate the JSON: Schemas setting. It is recommended you add this configuration only for the local workspace.

"json.schemas": [
	{
		"fileMatch": [
			"data/**/*.json"
		],
		"url": "./test/schema-template/schema.json"
	}
]

Version bump

Do npm run version-bump -- [OPTION], where [OPTION] is one of the following:

  • major to increment the major version (1.2.3 will become 2.0.0)
  • minor to increment the minor version (1.2.3 will become 1.3.0)
  • patch to increment the patch version (1.2.3 will become 1.2.4)
  • a version number (like 1.2.3)

It will first run the tests and fail to increase the version if the tests fail. It will then automatically replace the version in the files where it needs to be replaced, create a commit with the message chore(version): bump and create a tag (in the form v1.2.3) at the commit. This feature can be easily disabled by doing npm config set git-tag-version false.


License

This project is licensed under the terms of the MIT license.

pf2etools's People

Contributors

billyhelms24 avatar cgahr avatar chris-davis-siv avatar dependabot[bot] avatar ebullient avatar fatebreak avatar fredtargaryen avatar hephaistos-official avatar jnosh avatar julgvoz avatar kieranns avatar l-heemann avatar mjschleckser avatar mrvauxs avatar pf2etools avatar revilowaldow avatar sciguymjm avatar simonlk1 avatar spamuel42 avatar spappz avatar swaters83 avatar vytorcalixto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pf2etools's Issues

BUG-98 Undefined attacks in creatures

Steps to reproduce
Creatures have an issue where above their attack, a written "undefined" appears.

Severity
Low

Additional information
I found that deleting the getAttacks from creatures renderer also removes the undefined, so it is most likely a bug in there.

  • MrVauxs#8622
    Verification: 0

BUG-127 Text Converter - Space Bugs Edition

Steps to reproduce
A rather annoying quirk of the Text Converter is that everything that isn't the actual description of something has to NOT end with a space or it'll break.

It often happens with beginning [name] [type] [level] , but I remember it happening in other places as well.

You can use any of the sample items and just add a space to the end of their first line.

Severity
Low

Additional information
It would be neat to have the text converter either ignore or highlight such vestigial spaces, as they are always a problem of copying straight from a PDF rather than user error.

Also some checking of what came out of the converter would be good, as often spaces infer that the line hasn't ended. This can cause something like "bulk": "L This item looks like an ordinary glass flask full of water.", because it had Bulk L right before the start of the item description in it's text form.

  • MrVauxs#8622
    Verification: 0

BUG-139 "View this feat on the Feats page" broken

Steps to reproduce
Go to either Ancestry or Class page and when browsing the entries feats, click on the aforementioned button. It doesn't do anything.

Severity
Medium

Additional information
"A"

  • MrVauxs#8622
    Verification: 0

FEAT-103 #changelogs channel in the discord

Information
Have a ||5eTools|| style changelogs channel where major releases of the site ping users with an in-discord changes summary. This would be in addition to #git

Who would use it?
All server users, opt in/out role.

How would it work?
Ask Dusk.

Why should this be added?
Makes people aware of major updates and drives server engagement as people are pulled back in.

Upvote - revilowaldow#1

Upvote - spap#9812

Votes: +2 / -0 / ±0

BUG-91 testbug4

Steps to reproduce
testbug4

Severity
testbug4

Additional information
testbug4

  • LordDusk#0001
    Verification: 0

BUG-100 Class & Archetypes Page Broken

Steps to reproduce
Open the archetypes page (on dev)

Severity
Critical

Additional information
Uncaught TypeError: Cannot convert undefined or null to object at archetypes.js:313
Most likely in relation to our last removal of render-X

  • MrVauxs#8622
    Verification: 0

BUG-140 Text Converter "Effect"

Steps to reproduce
When converting an entry with "Effects" or "Effect" in it's description, the Converter breaks with Unexpected token of type "EFFECT"! Expected "SENTENCE_SEMICOLON_NEWLINE,SENTENCE_SEMICOLON,SENTENCE_TERM_NEWLINE,SENTENCE_TERM,SENTENCE_NEWLINE,SENTENCE". at Converter._consumeToken (http://localhost:8080/js/converter.js:1771:51)

Severity
Medium

Additional information
"A"

  • MrVauxs#8622
    Verification: 0

FEAT-107 Granularize Items page Price filter

Information
Simply make it possible to search by a specific amount of gold rather than preset amounts. See the other site for how it can be done.

Who would use it?
Everyone on the Items page looking for equipment within their budget.

How would it work?
As usual, just with more options.

Why should this be added?
Actually allows for accurate tracking of what items you can purchase than have to round it up and down. What if I have 75 gp? I must either limit myself down to 50 gp or have a lot of items that are above that budget by picking the 100 gp filter.

  • MrVauxs#8622

Indifferent - spap#9812

Upvote - TopHatG#8946

Votes: +1 / -0 / ±1

BUG-146 Filtering by alignment in the bestiary doesn't bring up any options.

Steps to reproduce
Go to the bestiary, filter by any of the specific alignments (e.g. Chaotic Good). There should be results (the Anadi Sage is chaotic good, but it doesn't show up) but nothing is returned

Severity
Low

Additional information
Filtering by not a specific alignment includes creatures with that disallowed alignment, so I imagine it thinks that creatures don't have an alignment at all. Filtering by 'any' alignment does seem to work though, as it brings up a single result.

  • AchillesUltimate#8629
    Verification: 0

BUG-148 Cannot scale creatures

Steps to reproduce
Go to Bestiary
Try to scale a creature
Get Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '2')

Severity
High

Additional information
"A"

  • MrVauxs#8622
    Verification: 0

BUG-119 All Lists are broken

Steps to reproduce
Rune Builder, Items Page, Spells page, Rituals page, you name it. You can add things, you can subtract things, but you cannot remove things. The only solution to this problem is the nuclear one, which is Delete All.

Severity
High

Additional information
Especially annoying in the Rune Builder, where you cannot remove a rune after adding it in.

  • MrVauxs#8622
    Verification: 0

BUG-116 Text Converter vs. Spells page

Steps to reproduce
TC's Saving Throws crash the Spells page.
TC converts Range in a way that is not rendered.
Spell Components are an object instead of an array of objects.

Severity
High

Additional information
Not really.

  • MrVauxs#8622
    Verification: 0

BUG-120 Homebrew Traits don't get categorized

Steps to reproduce
As the title implies, homebrew traits do not get categorized into proper trait categories in filters. You can test this with Rabbitfolk and the Rabbitfolk trait, which should be in Ancestries & Heritages, but it instead is in the General category.

Severity
Low

Additional information
The data of the feat is exactly the same as any other Ancestry & Heritage trait, but with a different name and homebrew source.

  • MrVauxs#8622
    Verification: 0

BUG-145 Data - Eidolon

Steps to reproduce
When browsing the Summoner page, all "data" entries referencing eidolon statblocks return undefined on the main site.

I cannot replicate this on the bug or dev branch.

Severity
High

Additional information
???

  • MrVauxs#8622
    Verification: 0

FEAT-118 Add a common and uncommon filter to the archetypes page

Information
It would be nice to filter out common or uncommon on the archetypes page

Who would use it?
Anyone looking at archetypes

How would it work?
Same as the other filters... just for common and uncommon

Why should this be added?
It would help whittle down the uncommon choices for players trying to pick an archetype, especially for free archetype games

  • lhx#2040

Upvote - spap#9812

Votes: +1 / -0 / ±0

BUG-133 Printing

Steps to reproduce
When printing... anything, you can see on the preview that text more often than not gets cut off if it has to go beyond a single page.

I think it's best if it moved the entire paragraph to the next page if it were to be cut off. Or at least, make it so text itself is not cut in half or otherwise cropped, and have actual margins on the next page.

Severity
Low

Additional information
Would be a neat feature with that fixed.

  • MrVauxs#8622
    Verification: 0

BUG-152 Text Converter - Spell Durations

Steps to reproduce
When a spell duration says "until the start of your next turn", the Text Converter doesn't recognize that as a "timed" duration (instead putting "unit": "unknown"), and thus does not get rendered on the sheet.

It should be parsing that as "type": "timed", and with a duration of 1 round.

Severity
Low

  • MrVauxs#8622
    Verification: 0

BUG-138 Text Converter - Speed Surge

Steps to reproduce
The Text Converter erronerously converts pretty much any ability starting with the word "Speed" into a speed entry rather than an ability. See Speed Surge in Drakes, for example.

Severity
Medium

Additional information
Filling this out because the bot otherwise breaks.

  • MrVauxs#8622
    Verification: 0

BUG-144 Encounter Builder with a giant hitbox

Steps to reproduce
Advanced Mode checkbox has a massive "hitbox" when it comes to it's width. You can click on the middle lower portion of the menu and enable it by accident.

Severity
Low

Additional information
Just an annoyance, but still one that exists.

  • MrVauxs#8622
    Verification: 0

BUG-143 Text Converter - Spell Domains

Steps to reproduce
Spell Domains are still converted as Cleric subclasses while they should be in their own separate "domains" array.

Severity
Medium

Additional information
"A"

  • MrVauxs#8622
    Verification: 0

BUG-96 Get link to Filters with List does not work

Steps to reproduce
Add spells to a list on the Spells Page
Get link to Filters with Shift on
Copy paste it into another tab or browser, preferably incognito
See that the list does not populate and we get Cannot read properties of undefined (reading '_multiSource') errors

Severity
Low

  • MrVauxs#8622
    Verification: 0

Cantrips aren't related to spell level

There is an issue with the level for cantrips as it is labeled as level 0 even if the spell is not 0th level.

Spells affected:
Almost all cantrips have this issue.

Example:
Allegro is level 7 but also a cantrip

BUG-125 Encounter Builder Doesn't Work

Steps to reproduce
Switch to Encounter Builder mode in the Bestiary, then attempt to add a creature to the list

Severity
Medium

Additional information
When attempting to add a creature to the list, it says "Please first view something from the list" and doesn't add anything. Attempting to remove a creature from the list does nothing, nor does increasing how many of the creature.

  • Ethmaker#5998
    Verification: 0

BUG-104 Often inaccurate price filters

Steps to reproduce
Open the Items page and select a price filter
See that the filtered item prices don't match up with what the filter says. 11 gp items are shown on ">25 gp" filter, or 30 gp items on ">50 gp".

Severity
Medium

Additional information
Honestly I think this warrants a re-do of the filter but my attempts to make it any better led me to crashing the website with the browser running out of memory.

  • MrVauxs#8622
    Verification: 0

FEAT-126 Text Converter - Rituals

Information
As any other entity, it would be great to have these done so we can convert everything over from books smoothly and efficiently.

Who would use it?
Every converter

How would it work?
As any other entity

Why should this be added?
Efficiency of converting stuff, as well as simply having a feature complete Text Converter with the two other suggestions (Vehicles, Hazards)

  • MrVauxs#8622
    Votes: +0 / -0 / ±0

BUG-93 generate-feat-trees not fully working

Steps to reproduce
See Shielded Tome and Raise a Tome feats. They should lead to eachother, but they are not.

Severity
Low

Additional information
Not sure why it fails to tag these while others are correct, especially when there are feats with exactly same structure (say, Miniaturize and Shrink Down)

  • MrVauxs#8622
    Verification: 0

BUG-149 Bestiary: printer view not working

Steps to reproduce

  1. Open bestiary.
  2. Add "Adult Black Dragon" and "Adult Blue Dragon" with the plus button to the list.
  3. Press "Printer view" button.
  4. Observe as nothing happens.

Severity
High

Additional information
Browser: Google Chrome (v 104.0.5112.81)
Extensions: tested after disabling all extensions and deleting cookie data

The following error is logged into console:

utils-list.js:1 Uncaught (in promise) TypeError: ListUtil.getSublisted is not a function
at Object.genericPinKeyMapper (utils-list.js:1:21577)
at PrintModeView.popTblGetNumShown (bestiary.js:17:5689)
at PrintModeView._renderContent (shared.js:70:29839)
at PrintModeView.pOpen (shared.js:75:104)
at PrintModeView.pHandleSub (shared.js:75:805)
at BestiaryPage.pDoLoadSubHash (bestiary.js:17:590)

  • solonkovda#6481
    Verification: 0

BUG-95 Same name traits inaccessible

Steps to reproduce
E.g. The clockwork traits
If the content has the same name it is not possible to access a version with a separate source.
The url does not include a fragment for the source and so will only return the first version no matter which is clicked on the site.
The same behaviour is seen in the popouts from the omnisearch

Severity
Medium

Additional information
https://pf2etools.com/traits.html#%5bclass%5d,fbsr:clockwork

BUG-108 Item Price Filter spammed with Undefined

Steps to reproduce
Open the Items page
Open the filter
See the price range going from your usual numbers to about 300 NaNs (in console, they're undefined)

Severity
Low, the filter is functional just, incredibly bogged down by the fake entries

Additional information
It doesn't seem to be anything related to the item filters themselves, but the RangeFilter function?
Whatever the options in the Price RangeFilter, the undefined spam stays.

  • MrVauxs#8622
    Verification: 0

BUG-92 text: search broken on items page

Steps to reproduce
Attempt to use the text: search on the items page

Severity
Medium, breaks non-essential a feature

Additional information
Uncaught TypeError: parts.push(...).map is not a function
render:7423

  • MrVauxs#8622
    Verification: 0

BUG-147 Spells page broke

Steps to reproduce
Open up the Spells page

Severity
Medium?

Additional information
No spells are loading, clear cache doesn't seem to fix it

  • kierann#6245
    Verification: 0

BUG-155 J/K Functionality on Archetypes Page

Steps to reproduce

  1. Open Archetypes page
  2. Select an Archetype
  3. Try pressing J/K keys

Expected Behaviour: Row Navigation Down and Up accordingly
Witnessed Behaviour: Navigation response absent

Severity
Low

Additional information
Continuing from StR
3.1. Open Feats sub-window.
3.2. Try pressing J/K keys

Expected Behaviour: Row Navigation Down and Up accordingly
Witnessed Behaviour: Navigation response absent, sub-window collapses and closes.

  • Hong Bo#9103
    Verification: 0

BUG-122 Unfinished phasing out of Creature Types

Steps to reproduce
Currently the filters depend on Trait Categories. The new Text Converter also assumes that the page depends on those, so it does not add the "creatureType" array to the statblock.

Unfortunately, the Bestiary page's middle column "Type" does require that array. It shouldn't.

Severity
Medium

Additional information
Any new creatures added through the Text Converter suffer from not having their type shown on the list.

  • MrVauxs#8622
    Verification: 0

BUG-118 Class Filters

Steps to reproduce
Filtering almost anything other than sources on the classes page causes class features and subclasses to disappear. This shouldn't really happen.

Severity
Medium

Additional information
This limits the page's filtering ability quite a bit, as you cannot view a class in 100% when filtering.

  • MrVauxs#8622
    Verification: 0

BUG-123 Quick Reference page loading forever

Steps to reproduce
go to quick reference page on pf2e

Severity
High

Additional information
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'forEach')
at Object.getEntryIdLookup (shared.js:160:17908)
at Object.showBookContent (bookutils.js:1:6248)
at doPopulate (bookutils.js:1:19683)
at Object.pLoadBook (bookutils.js:1:19964)
at async pHandleFound (bookutils.js:1:16768)

  • Coul#3188
    Verification: 0

FEAT-106 Spell References

Information
Currently spells can only tell that they belong to a class, subclass or a domain. We should be able to do the opposite and have the class data dictate what spells belong to it.

This can be expanded to a generic "grantsSpell" array or object, that can be applied to feats or heritages for example. With that expansion, it would be wise to add a References tab to spells, much like Traits have.

Who would use it?
Converters/Homebrewers

How would it work?
As explained in Information, we need a function that takes an array or object containing spells from class, subclass, feats, heritages, or other data that may come to mind, and implement it into the spells data that they list (a feat gives the Daze spell, the Daze spell lists the feat as a reference on the spell page).
See the mother site for examples on how they deal with class, subclass and race spells.

Why should this be added?
Sanity and ability to fully dictate to whom and where spells belong to.

  • MrVauxs#8622

Upvote - revilowaldow#1

Upvote - spap#9812

Upvote - TopHatG#8946

Upvote - agateophobia.#5149

Upvote - _Costa#1356

Upvote - BiSP#8304

Votes: +6 / -0 / ±0

BUG-138 Text Converter: attacks are not properly parsed

Steps to reproduce

  1. Input the text for the Sample Creature (seen in Help tab, which can be opened with the button in the bottom left)
  2. Click Parse
  3. Observe that the line corresponding to the creature's attack:
Melee [one-action] hand +24 (finesse, magical), Damage 4d8 negative plus paralyzing touch

is converted into

"attacks": [
	{
		"range": "Melee",
		"activity": {
			"number": 1,
			"unit": "action"
		},
		"traits": [
			"finesse",
			"magical"
		],
		"name": "hand",
		"bonus": 24,
		"damage": "{@damage 4d8} negative plus paralyzing touch"
	}
]

Notably, it has "bonus": 24 instead of "attack": 24, and it is missing types:

"types": [
	"negative"
]

When viewing the added creature in Bestiary, it shows up like this:
image
Instead of this:
image

Severity
Low

BUG-105 Double Book Index when using built-in brew

Steps to reproduce
Add a homebrew file containing a book to the PF2eT homebrew folder and the index.json file
Open the book in the browser
See the error, two identical indexes, on the left

Severity
Low

  • MrVauxs#8622
    Verification: 0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.