Code Monkey home page Code Monkey logo

model-keyword's Introduction

model-keyword

Automatic1111 WEBUI extension to autofill keyword(trigger words) for custom stable diffusion models and LORA models.

model-keyword-github.webm

Installation

Copy the url of the repository ( https://github.com/mix1009/model-keyword ) into the extension tab and press "Install"

Screenshot 2022-12-01 at 12 14 25 PM

From "Extensions/Installed" tab press "Apply and restart UI". Screenshot 2022-12-01 at 12 18 43 PM

Usage

From txt2img, img2img tab, "Model Keyword" section is added. Model keyword extension is enabled by default. Click Model Keyword or triangle to reveal options.

model-keyword-open

When generating image, the extension will try to figure out which model is used and insert matching keyword to the prompt: model-keyword-usage

Keyword placement

Screenshot 2022-12-01 at 12 26 41 PM

Multiple keywords

Screenshot 2022-12-01 at 12 27 00 PM

  1. keyword1, keyword2 - use all keywords separated by comma
  2. random - choose one random keyword
  3. iterate - iterate through each keyword for each image generation
    • If sd-dynamic-prompts extension is installed, iterate will not work properly. Please disable Dynamic Prompts.
    • Alternatively, you can rename model-keyword to sd-model-keyword in the extensions folder. It will change the order of the extension and fix the bug.
  4. keyword1 - use first keyword
  5. keyword2 - use second keyword (if it exists)

LORA model support

LORA

  1. Select LORA model from the Model dropdown.
  2. Keywords dropdown list should contain keywords for the selected model.
  3. Limitation: You can only select one model, and select one keyword or all keywords.
  4. If you are using multiple LORA models, please check https://lora-help.coolai.app/ .

Add Custom Mappings

custom_mappings

  • "Check" -> outputs model filename, hash, matching keyword(s), and source of match in result.

  • "Save" -> save custom mapping with keyword. (Fill keyword)

  • "Delete" -> deletes custom mapping for model if it's available.

  • Mappings are saved in custom-mappings.txt

  • If previous mapping is found, save overwrites it.

  • do NOT edit model-keyword.txt . It can be overwritten or cause conflict while upgrading.

  • hash value for model has been changed in webui(2023-01-14), this extension uses old hash value. Old hash value is no longer displayed in webui.

model-keyword's People

Contributors

mix1009 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

model-keyword's Issues

Message settings2 with each generated image

I updated the extension version today (January 31, 2023) and a message started to appear with each generated image:
settings2 = {'is_enabled': 'True', 'keyword_placement': 'keyword prompt', 'multiple_keywords': 'keyword1, keyword2', 'ti_keywords': 'None', 'keyword_order': 'textual inversion first'}

This extension does not work with the OpenGen V1.0 model

Hi, this model obviously has a Trigger Words for different styles, but Keyword has never added to PNG information (metadata). I am doing something wrong? I tried some Lora model at random and Keywords found it without any problems.

not an issue but a question

hello, sorry i couldnt find a discussion tab for this extension,
what Multiple keywords is made for and how does it work ?

[Feature Request] Lora support?

With Loras gaining massive speed (and small size), they are growing in popularity. Mostly similar to the checkpoint models, they use trigger words... so a hash/trigger word approach would be needed. Pretty much like a middle ground between TIs and Checkpoint models.

Loras only get loaded on prompt start, so either adding them via model keyword (similar to TIs), or detecting they are in prompt as lora:Lorafilename:1 (# is strength, can be floating and more or less than 1)

Error completing request Arguments:

Error completing request
Arguments: ('lion', '', 'None', 'None', 20, 0, False, False, 1, 1, 7, -1.0, -1.0, 0, 0, 0, False, 512, 512, False, 0.7, 0, 0, 5, False, False, False, '', 1, '', 0, '', True, False, False, '

You can edit extensions/model-keyword/model-keyword-user.txt to add custom mappings

', 'keyword prompt', 'keyword1, keyword2', 1.0, 2.0, 'a painting in', 'style', 'picture frame, portrait photo', None) {}
Traceback (most recent call last):
File "D:\stable-diffusion-webui\modules\call_queue.py", line 45, in f
res = list(func(*args, **kwargs))
File "D:\stable-diffusion-webui\modules\call_queue.py", line 28, in f
res = func(*args, **kwargs)
File "D:\stable-diffusion-webui\modules\txt2img.py", line 46, in txt2img
processed = modules.scripts.scripts_txt2img.run(p, *args)
File "D:\stable-diffusion-webui\modules\scripts.py", line 317, in run
processed = script.run(p, *script_args)
File "D:\stable-diffusion-webui\extensions\stable-diffusion-webui-inspiration\scripts\inspiration.py", line 39, in run
for file in files:
TypeError: 'NoneType' object is not iterable

Multiple keywords modality "Iterate" generates a single image

I'm testing the new Comic Diffusion v2 model with 6 activation tokens.

I set up the Multiple Keywords mode to Iterate, as I want A1111 to produce 6 images, one for each activation token.
As I click Generate, tho, only a single image is produced.

Did I misunderstand the intent of the iterate mode?

I have a question about Lora feature

a11

Lora Keywords are not added when I use the Default Lora functions of WEBUI.

Should I manually select Lora and keywords in the Model-keyword extension dropdown menu?

I hope keyword add automatically when I use Default Lora Functions of WEBUI.

a2

Extension doesn't recognize Lora in the subfolder.

models\Lora\Vehcile

In this case, Lora in the Vehicle folder doesn't recognized in Model-Keyword Extension.

model-keyword-user.txt overwritten on updates

Hi,
Whenever this extension is updated through Web UI, the model-keyword-user.txt is overwritten back to the default file.
I'm not sure how Web UI updates extensions, but please consider how this file is handled, so it doesn't get overwritten each update.

requests

can there be included a way to add keywords inside auto1111, without going to the user script?
can there be a menu that drops down that tells you all the keywords associated with the model before you generate?
thank you

Extension runs as enabled by default

Most extensions are disabled by default when first installed, this one is opposite - its enabled by default as soon as its installed.
If possible, please change default behavior.

Close with error when try to use embeddings options on MacOS

Is there an existing issue for this?

I have searched the existing issues and checked the recent builds/commits, but I couldn't find a similar issue.

What happened?

When I try to use/set embeddings option it closes with error

/AppleInternal/Library/BuildRoots/9e200cfa-7d96-11ed-886f-a23c4f261b56/Library/Caches/com.apple.xbs/Sources/MetalPerformanceShadersGraph/mpsgraph/MetalPerformanceShadersGraph/Core/Files/MPSGraphExecutable.mm:1309: failed assertion `Incompatible element type for parameter at index 0, mlir module expected element type f32 but received f16'

[1] 16300 abort ./webui.sh --administrator

Steps to reproduce the problem

Run the process "C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py"
What should have happened?

The process should have run without any errors.

Commit where the problem happens

commit d04e3e921e8ee71442a1f4a1d6e91c05b8238007 (HEAD -> master, origin/master, origin/HEAD)
Author: AUTOMATIC [email protected]
Date: Sat Jan 28 15:24:29 2023 +0300

What platforms do you use to access UI ?

MacOS

What browsers do you use to access the UI ?

Google Chrome

Command Line Arguments

--administrator --upcast-sampling --use-cpu interrogate

Additional information, context and logs

none

Thank you.

Request: trigger word sets/groups within each LoRA

Ideally, this extension should be able to handle conceptual groups of trigger words within a single LoRA, such as:

EvangelionLoRA.safetensors

AYANAMIREI, EVA00PLUGSUIT, INTERFACE HEADSET

AYANAMIREI, TOKYO-3_MIDDLE_SCHOOL_UNIFORM, RED RIBBON, SCHOOL_UNIFORM, BLACK SOCKS

SORYUASUKALANGLEY, TOKYO-3_MIDDLE_SCHOOL_UNIFORM, RED RIBBON, SCHOOL_UNIFORM, WHITE SOCKS, INTERFACE HEADSET

SORYUASUKALANGLEY, EVA02PLUGSUIT, INTERFACE HEADSET

--//--

Description 'keyword or keywords separated by |' is unclear.

Expected behavior from description might be:

word1, word2, word3|word1, word4, word5

Which should produce a drop list:

word1, word2, word3
word1, word4, word5

But in actual fact, produces a drop list:

word1

...because the underlying text file is CSV and the input is not sanitized.

Alternative expected behavior from description might be:

word1|word2|word3|word4|word5

But then the user does unnecessary extra work for no gain (shift key) to type it rather than naturally type (or copy/paste) 'word1, word2, word3, word4, word5' and have the extension convert the ", " or "," to "|" when writing to the file (Replace and Trim).

But this still cannot handle LoRA that conceptually have multiple sets of trigger words that are only used together in groups, such as multiple named characters with multiple costume options.

--//--

Although the README states that saving new sets of keywords overwrites the previous saved entries, the content of the file is not overwritten but appended in practice.

So, it appears possible to save groups as multiple entries in the underlying text file by:

word1|word2|word3 [SAVE BUTTON]
word4|word5|word6 [SAVE BUTTON]

And the file will then contain:

checksum1234, word1|word2|word3, LoRA1.safetensors
checksum1234, word4|word5|word6, LoRA1.safetensors

But, when the file is read back into the drop list, only the last entry for the LoRA is listed and all other entries for the same LoRA are ignored:

word4
word5
word6

I suspect this behaviour is a bug I've accidentally discovered by trying to figure out how to group trigger words.

--/--

BTW the extension fails with an error if the text file accidentally becomes UTF-8, which is the default format for Windows 11 onwards. This is going to catch people out, as they will want to edit the text files manually to correct mistakes, as this will be much faster than starting up and using the WebUI interface when making alterations to LoRA collections.

I discovered this problem while trying to work around the grouping problem using 'word1·word2·word3|word4·word5' with the intent to manually edit after auto-insertion (note: some trigger words do contain spaces, so space cannot be used to separate words in a group).

--//--

This add-in has potential to become a major sought-after feature that can overcome the problem that the WebUI lacks any form of trigger word management other than having a huge unsearchable Style list.

I have not seen any alternatives so far, other than keeping a notebook on the side. The website you mentioned in another thread is not up-to-date with the large number of new LoRA being produced and also requires users to have not renamed the LoRA to make them easier to use.

I do hope you will be encouraged to continue work on this (and check out the booru tag autocomplete extension to see if the same techniques can be used to provide pop-up trigger word selection drop lists in the prompt box itself rather than using a drop list of LoRAs below it).

[Feature Request] Replace trigger in prompt

Hello,
This extension is fantastic for working with a large amount of trained models, but I was wondering if it would be possible to have a mode that replaces some trigger word with the pre-set keyword for a model. For example:
A photo of $trigger on a scenic mountaintop
or
A charcoal drawing of an army of ducks following $trigger with a pan flute
would replace $trigger with whatever keyword was associated with the model. This way the keyword could come anywhere in the prompt rather than always being pre/appended.

I naively think this could be as simple as replacing:

            if keyword_placement.startswith('keyword'):
                arr.append(prompt)
            else:
                arr.insert(0, prompt)

with something like

            trigger = 'xyz'
            if keyword_placement.startswith('keyword'):
                arr.append(prompt)
            elif 'replace' in keyword_placement:
                arr = prompt.replace(trigger, ''.join(arr))
            else:
                arr.insert(0, prompt)

and the relevant menu options, but I may be missing something.

Thanks!

Failure to run extension due to missing custom-mappings.txt file - workaround is to create one

(BTW, this is just the extension I needed when I did a big huge X/Y plot run to compare 50 some models. Running again.)

After I installed the extension, it would not run initially without creating a blank custom-mappings.txt file.

Can a blank one be created and installed by default?

Repro:

  • Install extension
  • Restart UI
  • I started an X/Y Plot that begins w/ the analog-diffusion model
  • Generate
    Result:
  • Traceback error to command console saying that custom-mappings.txt was not found
    Workaround:
  • Create a blank custom-mappings.txt file

Expanding the script extension area, too, and performing Save doesn't help. Performing Check gets an error.

Traceback:
Traceback (most recent call last):
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\gradio\routes.py", line 337, in run_predict
output = await app.get_blocks().process_api(
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\gradio\blocks.py", line 1015, in process_api
result = await self.call_function(
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\gradio\blocks.py", line 833, in call_function
prediction = await anyio.to_thread.run_sync(
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\anyio\to_thread.py", line 31, in run_sync
return await get_asynclib().run_sync_in_worker_thread(
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\anyio_backends_asyncio.py", line 937, in run_sync_in_worker_thread
return await future
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\venv\lib\site-packages\anyio_backends_asyncio.py", line 867, in run
result = context.run(func, *args)
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py", line 122, in check_keyword
entry = get_keyword_for_model(model_hash, model_ckpt, return_entry=True)
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py", line 71, in get_keyword_for_model
hash_dict = load_hash_dict()
File "C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py", line 41, in load_hash_dict
modified = str(os.stat(default_file).st_mtime) + '_' + str(os.stat(user_file).st_mtime)
FileNotFoundError: [WinError 2] The system cannot find the file specified: 'C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\extensions\model-keyword/custom-mappings.txt'

[Feature Request] Embedding Trigger words

Thank you for sharing this extension.

It would be great to have the same functionality for Embedding/Textual Inversions keywords and notes.

I hope @civitai can provide a list of Embedding, trigger words and hashes.

LORA's broken

LORAs selected by the dropdown menu; and attempting to use created custom keyword mapping results in 'ERROR" in the UI.

NameError: name 'model_ckpt' is not defined
Traceback (most recent call last):
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\gradio\routes.py", line 337, in run_predict
output = await app.get_blocks().process_api(
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\gradio\blocks.py", line 1015, in process_api
result = await self.call_function(
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\gradio\blocks.py", line 833, in call_function
prediction = await anyio.to_thread.run_sync(
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\anyio\to_thread.py", line 31, in run_sync
return await get_asynclib().run_sync_in_worker_thread(
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\anyio_backends_asyncio.py", line 937, in run_sync_in_worker_thread
return await future
File "G:\stable-diffusion-webui-current\venv\lib\site-packages\anyio_backends_asyncio.py", line 867, in run
result = context.run(func, *args)
File "G:\stable-diffusion-webui-current\extensions\model-keyword\scripts\model_keyword.py", line 444, in add_lora_keyword
lora_keywords = get_lora_keywords(lora_model)
File "G:\stable-diffusion-webui-current\extensions\model-keyword\scripts\model_keyword.py", line 168, in get_lora_keywords
kws = _get_keywords_for_lora(lora_model)
File "G:\stable-diffusion-webui-current\extensions\model-keyword\scripts\model_keyword.py", line 157, in _get_keywords_for_lora
sim = str_simularity(kw_ckpt[1], model_ckpt)
NameError: name 'model_ckpt' is not defined

extension not working with "one button prompt" extension

So it makes sense that these two won't play nice together, but I figured I'll post the error here and see if its fixable.
One button prompt is a fun prompting extension - https://github.com/AIrjen/OneButtonPrompt
When using it with model-keyword enabled I get the following in console:

Error running process: F:\SD\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py Traceback (most recent call last): File "F:\SD\stable-diffusion-webui\modules\scripts.py", line 451, in process script.process(p, *script_args) File "F:\SD\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py", line 657, in process p.prompt = new_prompt(p.prompt, kw, no_iter=True) File "F:\SD\stable-diffusion-webui\extensions\model-keyword\scripts\model_keyword.py", line 649, in new_prompt return ', '.join(arr) TypeError: sequence item 1: expected str instance, list found

Thank you for your work on this extension!
(also - please add lycrois support :) )

[Feature Request] Can Lora models dropdown be ordered alphabetically?

See the example below.

None
anime_nausicae.safetensors
artist_dan_mumford.safetensors
illustration_kSUWABESTYLE_v2.safetensors
illustration_enaic.safetensors
special_epiNoiseoffset_v2.safetensors
special_offset.safetensors
style_anaglyph.safetensors
style_rotoscopee.safetensors

I organize the Loras with types before their names.

Integration with Plot X/Y?

This is a great extension that will save me tons of time. Thanks for sharing it with the community.

Is there any chance to integrate it, somehow, with the Plot X/Y script?

Plot X/Y already allows to call different checkpoints/models but there's no way to automatically include the relevant keyword for each checkpoint in the prompt.

If these two extensions could work together, it would be amazing.

Thank you for keeping this going.

Hello, just wanted to thank anyone involved in keeping this project going. It's one of the longest-running extensions and still going strong, and I believe it is indispensable to many.

Absolutely awesome, you rock. <3

The system cannot find the file specified

Is there an existing issue for this?
I have searched the existing issues and checked the recent builds/commits, but I couldn't find a similar issue.

What happened?
I am getting the following error when running the process: C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py

Traceback (most recent call last):
File "C:\sd\webui\modules\scripts.py", line 347, in process
script.process(p, *script_args)
File "C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py", line 327, in process
kw = self.get_keyword(model_hash, model_ckpt)
File "C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py", line 243, in get_keyword
hash_dict = self.load_hash_dict()
File "C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py", line 213, in load_hash_dict
modified = str(os.stat(default_file).st_mtime) + '_' + str(os.stat(user_file).st_mtime)
FileNotFoundError: [WinError 2] The system cannot find the file specified: 'C:\sd\webui\extensions\model-keyword/custom-mappings.txt'

Steps to reproduce the problem

Run the process "C:\sd\webui\extensions\model-keyword\scripts\model_keyword.py"
What should have happened?

The process should have run without any errors.
Commit where the problem happens

My Webui is on Commit hash a8322ad.

What platforms do you use to access UI ?
Windows

What browsers do you use to access the UI ?
Google Chrome

Command Line Arguments
None

Additional information, context and logs
none

Thank you.

Support for Non-unicode characters?

image

Can't seem to add Chinese characters or at least what I think are chinese characters: 可露凯
I assume it has something to do with character encoding or something.

Comic Diffusion v2 issue with current token

Comic Diffusion v2 [8b3c8f111] is currently with token comicmay artsyle. However, per instructions here there are 6 activation token and none of them matches the one currently used:

charliebo artstyle
holliemengert artstyle
marioalberti artstyle
pepelarraz artstyle
andreasrocha artstyle
jamesdaly artstyle

Not sure how you'll implement this :) Ideally, somebody would want to be able to pick which one of the 6 to enforce.

README - I had to use a different path to install the extension in Automatic1111

I tried to install the extension given the README directions but I got a git error via A1111 when trying to do so (I have git bash installed and it is accessible through the PATH).

FWIW, I did install by giving the GitHub clone path to A1111:
https://github.com/mix1009/model-keyword.git

I restarted the UI and the extension is installed.

This is on Windows 10.

Command line output:
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v https://github.com/mix1009/model-keyword C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\tmp\model-keyword
stderr: 'fatal: could not create work tree dir 'C:\Users\Eric\repos\Stable-Diffusion\stable-diffusion-webui\tmp\model-keyword ': Invalid argument

Request - Detect loras in prompt and display/insert related keywords in box

Using this extension IMHO is very nonintuitive. Ideally, what we all want is a way to easily see and add related keywords for certain loras, models, etc. without needing to remember or look up the keywords online. The way it works currently has a lot of issues that conflict with this vision though.

The issues - From my perspective

  1. You need to manually select from a dropdown what lora you want to use keywords from. This dropdown use text to search so you need to manually scroll and find the lora. If you have 100+ loras this becomes extremely tedious.
  2. After selecting a lora you need to click the keyword dropdown just to see if any were even detected/saved.
  3. keyword selection of dropdown is limited. You can do all keywords, or individual keywords. What if there were only 3 keywords and you only wanted to use 2? The dropdown prevents that
  4. Because keywords are in a dropdown I can copy and past keywords to prompt
  5. Because you need to manually select the lora and keywords using dropdowns, this also means if you had multiple loras you can only use keywords from one at a time in your prompt
  6. Keywords added are added to the beginning of the prompt, but users might not always want them put there.

The solution

I think instead this detection should detect loras that exist in the prompt and show a list of keywords that can be used with each lora. Then the user can click to add whatever keywords they want from the list or lists (if multiple loras are used.)

I imagine this working very similarly to the way adding loras in webui works already. you could have a button that shows and hides the box of keywords. It should probably be positioned just under the prompt box since it will be adding keywords to the prompt.

It's a decent overhaul but i think it would be greatly beneficial.

Is it possible to save settings?

Right now I need to change "Multiple keywords:" to random (or whatever I want) every time I start the ui. I would prefer it to save my preferred setting.
I've looked through the settings but couldn't find an option for it. Sorry if it's just me being blind

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.