Initial commit: Tonalmine, a Material Design 3 theme for Redmine 7

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
surtic86
2026-10-01 06:05:29 +02:00
co-authored by Claude Opus 5.5
commit 06cd1db99c
47 changed files with 4302 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
# Builds tonalmine/images/icons.svg from tools/icon_map.yml.
# Usage (from repo root): ruby tools/build_icons.rb
# Ruby stdlib only. Downloads are cached in tools/.cache/.
require 'yaml'
require 'net/http'
require 'uri'
require 'fileutils'
CORE_ICONS = 'https://raw.githubusercontent.com/redmine/redmine/7.0-stable/config/icon_source.yml'
SYMBOLS = 'https://raw.githubusercontent.com/google/material-design-icons/master/symbols/web'
ROOT = File.expand_path('..', __dir__)
CACHE = File.join(ROOT, 'tools/.cache')
OUT = File.join(ROOT, 'tonalmine/images/icons.svg')
def fetch(url)
res = Net::HTTP.get_response(URI(url))
res.is_a?(Net::HTTPSuccess) ? res.body : nil
end
def fail_with(message, items)
warn "#{message}:\n #{items.join("\n ")}"
exit 1
end
# 1. Map must cover exactly the core icon names.
core_yml = fetch(CORE_ICONS) or fail_with('Could not fetch', [CORE_ICONS])
core = YAML.safe_load(core_yml).map { |e| e['name'] }.uniq
map = YAML.load_file(File.join(ROOT, 'tools/icon_map.yml'))
names = map.map { |e| e['name'] }
dupes = names.select { |n| names.count(n) > 1 }.uniq
fail_with('Duplicate names in icon_map.yml', dupes) if dupes.any?
missing = core - names
extra = names - core
fail_with('Missing from icon_map.yml', missing) if missing.any?
fail_with('Not in Redmine icon_source.yml (extra)', extra) if extra.any?
# 2. Download (or read cached) SVGs.
FileUtils.mkdir_p(CACHE)
not_found = []
symbols = map.sort_by { |e| e['name'] }.map do |e|
file = "#{e['symbol']}#{e['fill'] ? '_fill1' : ''}_24px.svg"
cached = File.join(CACHE, file)
unless File.exist?(cached)
svg = fetch("#{SYMBOLS}/#{e['symbol']}/materialsymbolsoutlined/#{file}")
next not_found << "#{e['name']} -> #{file}" unless svg
File.write(cached, svg)
end
paths = File.read(cached).scan(/<path\b[^>]*\bd="([^"]+)"/).flatten
fail_with('No <path> found in', [cached]) if paths.empty?
body = paths.map { |d| %(<path d="#{d}" fill="currentColor" stroke="none"/>) }.join
%(<symbol id="icon--#{e['name']}" viewBox="0 -960 960 960">#{body}</symbol>)
end
fail_with('Material Symbol not found', not_found) if not_found.any?
# 3. Write the sprite.
File.write(OUT, <<~SVG)
<svg xmlns="http://www.w3.org/2000/svg">
<!-- Generated by tools/build_icons.rb from tools/icon_map.yml. Do not edit by hand.
Icons: Material Symbols Outlined by Google (github.com/google/material-design-icons),
Apache License 2.0, see LICENSE-material-symbols.txt. -->
#{symbols.join("\n")}
</svg>
SVG
puts "Wrote #{symbols.size} icons to #{OUT.sub("#{ROOT}/", '')}"
+242
View File
@@ -0,0 +1,242 @@
# Redmine 7.0 icon name -> Material Symbols (Outlined, weight 400) name.
# Order follows Redmine's config/icon_source.yml. `fill: true` uses the filled variant.
# Rebuild the sprite with: ruby tools/build_icons.rb
- name: edit
symbol: edit
- name: add
symbol: add_circle
- name: copy
symbol: content_copy
- name: del
symbol: delete
- name: save
symbol: save
- name: download
symbol: download
- name: attachment
symbol: attach_file
- name: time-add
symbol: more_time
- name: time
symbol: schedule
- name: fav
symbol: star
- name: copy-link
symbol: link # note: core uses a clipboard; "link" reads better for "Copy link"
- name: 3-bullets
symbol: more_horiz
- name: history
symbol: history
- name: folder
symbol: folder
- name: folder-open
symbol: folder_open
- name: lock
symbol: lock
- name: unlock
symbol: lock_open
- name: link-break
symbol: link_off
- name: zoom-in
symbol: zoom_in
- name: zoom-out
symbol: zoom_out
- name: settings
symbol: settings
- name: news
symbol: newspaper
- name: user
symbol: person
- name: group
symbol: group
- name: bookmarked
symbol: bookmark
- name: bookmark-delete
symbol: bookmark_remove
- name: bookmark-add
symbol: bookmark_add
- name: import
symbol: database_upload
- name: summary
symbol: bolt # note: mirrors core's Tabler "bolt"
- name: angle-down
symbol: expand_more
- name: angle-right
symbol: chevron_right
- name: angle-up
symbol: expand_less
- name: angle-left
symbol: chevron_left
- name: email
symbol: mail
- name: email-disabled
symbol: mail_off
- name: stats
symbol: bar_chart
- name: reorder
symbol: reorder # note: Tabler "menu-order" has up/down arrows; Material is plain lines
- name: close
symbol: close # note: plain X, no square frame
- name: checked
symbol: check
- name: reload
symbol: refresh
- name: link
symbol: link
- name: plugins
symbol: extension
- name: roles
symbol: admin_panel_settings # note: Tabler "shield-cog"
- name: list
symbol: list
- name: workflows
symbol: account_tree # note: Tabler "jump-rope" has no Material equivalent
- name: server-authentication
symbol: dns
- name: table-multiple
symbol: refresh # note: mirrors core, which also draws "refresh" here
- name: projects
symbol: inventory_2 # note: Tabler "packages"
- name: project
symbol: inventory_2 # note: Tabler "packages"
- name: package
symbol: package_2
- name: custom-fields
symbol: dynamic_form # note: Tabler "input-check"
- name: help
symbol: info
- name: changeset
symbol: code
- name: clear-query
symbol: filter_alt_off # note: by meaning; core uses a square X
- name: warning
symbol: warning
- name: comments
symbol: forum
- name: comment
symbol: comment
- name: message
symbol: chat_bubble
- name: reply
symbol: reply # note: by meaning; core uses Tabler "messages"
- name: arrow-right
symbol: arrow_forward
- name: wiki-page
symbol: article # note: Tabler "notebook"
- name: bullet-end
symbol: arrow_circle_left
- name: bullet-go
symbol: arrow_circle_right
- name: bullet-go-end
symbol: diamond # note: Tabler "diamonds"
- name: move
symbol: move_item # note: Tabler "arrow-forward-up"
- name: cancel
symbol: undo
- name: document
symbol: description
- name: issue
symbol: sticky_note_2 # note: Tabler "note"
- name: issue-closed
symbol: check_box
- name: issue-edit
symbol: edit_square
- name: issue-note
symbol: add_comment
- name: file
symbol: draft
- name: text-plain
symbol: description
- name: text-x-c
symbol: code_blocks # note: generic code file
- name: text-x-csharp
symbol: code_blocks # note: generic code file
- name: text-x-java
symbol: code_blocks # note: generic code file
- name: text-x-php
symbol: php
- name: text-x-ruby
symbol: code_blocks # note: generic code file
- name: text-xml
symbol: code # note: generic markup
- name: text-css
symbol: css
- name: text-html
symbol: html
- name: application-javascript
symbol: javascript
- name: application-pdf
symbol: picture_as_pdf
- name: application-zip
symbol: folder_zip
- name: application-gzip
symbol: folder_zip
- name: chevrons-right
symbol: keyboard_double_arrow_right
- name: chevrons-left
symbol: keyboard_double_arrow_left
- name: key
symbol: key
- name: search
symbol: search
- name: toggle-plus
symbol: add_box
- name: toggle-minus
symbol: indeterminate_check_box
- name: circle-minus
symbol: do_not_disturb_on
- name: circle-dot-filled
symbol: radio_button_checked
fill: true
- name: watch
symbol: visibility
- name: unwatch
symbol: visibility_off
- name: copy-pre-content
symbol: content_paste
- name: thumb-up
symbol: thumb_up
- name: thumb-up-filled
symbol: thumb_up
fill: true
- name: alert-circle
symbol: error
- name: bulb
symbol: lightbulb
- name: message-report
symbol: feedback
- name: quote-filled
symbol: format_quote
fill: true
- name: loader
symbol: progress_activity
- name: hourglass
symbol: hourglass
- name: webhook
symbol: webhook
- name: apps
symbol: apps
- name: shield-check
symbol: verified_user
- name: arrow-narrow-left
symbol: arrow_left_alt
- name: arrow-narrow-right
symbol: arrow_right_alt
- name: photo
symbol: image
- name: movie
symbol: movie
- name: file-music
symbol: audio_file
- name: file-type-docx
symbol: docs # note: generic document with lines
- name: file-type-xls
symbol: table # note: generic spreadsheet
- name: file-type-ppt
symbol: slideshow # note: generic presentation
- name: maximize
symbol: fullscreen
- name: link-add
symbol: add_link
- name: unknown
symbol: help
+1
View File
@@ -0,0 +1 @@
.build/
+73
View File
@@ -0,0 +1,73 @@
// Generates tonalmine/stylesheets/palettes.css: one M3 color scheme per seed color,
// in light and dark. Run: cd tools/palettes && npm install && npm run build
// (bundled with esbuild because material-color-utilities 0.4.0 has extensionless ESM imports).
import { writeFileSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { Hct, SchemeTonalSpot, MaterialDynamicColors as C, hexFromArgb, argbFromHex, customColor } from '@material/material-color-utilities';
// The first palette is the default. Ids must match PALETTES in tonalmine/javascripts/theme.js.
const PALETTES = [
['purple', '#6750A4'],
['blue', '#0061A4'],
['teal', '#006A6A'],
['green', '#386A20'],
['orange', '#8B5000'],
];
const CUSTOM = [['success', '#386A20'], ['warning', '#7C5800']];
const ROLES = ['primary', 'onPrimary', 'primaryContainer', 'onPrimaryContainer', 'inversePrimary',
'secondary', 'onSecondary', 'secondaryContainer', 'onSecondaryContainer',
'tertiary', 'onTertiary', 'tertiaryContainer', 'onTertiaryContainer',
'error', 'onError', 'errorContainer', 'onErrorContainer',
'background', 'onBackground', 'surface', 'surfaceDim', 'surfaceBright',
'surfaceContainerLowest', 'surfaceContainerLow', 'surfaceContainer', 'surfaceContainerHigh', 'surfaceContainerHighest',
'onSurface', 'surfaceVariant', 'onSurfaceVariant', 'inverseSurface', 'inverseOnSurface',
'outline', 'outlineVariant', 'shadow', 'scrim', 'surfaceTint'];
const kebab = (s) => s.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
const hex = (argb) => hexFromArgb(argb);
function roles(seedHex, dark) {
const seed = argbFromHex(seedHex);
const scheme = new SchemeTonalSpot(Hct.fromInt(seed), dark, 0);
const lines = ROLES.map((r) => `--md-sys-color-${kebab(r)}: ${hex(C[r].getArgb(scheme))};`);
for (const [name, value] of CUSTOM) {
const c = customColor(seed, { name, value: argbFromHex(value), blend: true })[dark ? 'dark' : 'light'];
lines.push(`--md-sys-color-${name}: ${hex(c.color)};`, `--md-sys-color-on-${name}: ${hex(c.onColor)};`,
`--md-sys-color-${name}-container: ${hex(c.colorContainer)};`, `--md-sys-color-on-${name}-container: ${hex(c.onColorContainer)};`);
}
return lines.join(' ');
}
// Specificity: the default palette uses bare :root, the others add [data-palette], and every dark
// rule adds one more attribute, so "palette + mode" always beats "palette" and the default.
let css = `/*
* Generated by tools/palettes/build_palettes.mjs; do not edit by hand.
* M3 color roles (TonalSpot, contrast 0) per palette, light and dark.
* <html data-palette="..."> picks the palette, <html data-mode="light|dark"> forces a mode;
* without data-mode the OS setting (prefers-color-scheme) decides.
*/
:root { color-scheme: light; }
:root[data-mode="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) { :root:not([data-mode="light"]) { color-scheme: dark; } }
`;
PALETTES.forEach(([id, seed], i) => {
const sel = i === 0 ? ':root' : `:root[data-palette="${id}"]`;
css += `
/* ${id} (seed ${seed}) */
${sel} { ${roles(seed, false)} }
${sel}[data-mode="dark"] { ${roles(seed, true)} }
@media (prefers-color-scheme: dark) { ${sel}:not([data-mode="light"]) { ${roles(seed, true)} } }
`;
});
// Paths are relative to tools/palettes (npm runs scripts there); the bundle itself lives in .build/.
const theme = resolve(process.cwd(), '../../tonalmine');
const out = `${theme}/stylesheets/palettes.css`;
writeFileSync(out, css);
// Keep theme.js in sync: every palette id must appear in its PALETTES list.
const js = readFileSync(`${theme}/javascripts/theme.js`, 'utf8');
const missing = PALETTES.map(([id]) => id).filter((id) => !js.includes(`'${id}'`));
if (missing.length) { console.error(`theme.js is missing palettes: ${missing.join(', ')}`); process.exit(1); }
console.log(`Wrote ${PALETTES.length} palettes to ${out}`);
+505
View File
@@ -0,0 +1,505 @@
{
"name": "tonalmine-palettes",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tonalmine-palettes",
"devDependencies": {
"@material/material-color-utilities": "0.4.0",
"esbuild": "^0.25.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@material/material-color-utilities": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.4.0.tgz",
"integrity": "sha512-dlq6VExJReb8dhjj3a/yTigr3ncNwoFmL5Iy2ENtbDX03EmNeOEdZ+vsaGrj7RTuO+mB7L58II4LCsl4NpM8uw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/esbuild": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.12",
"@esbuild/android-arm": "0.25.12",
"@esbuild/android-arm64": "0.25.12",
"@esbuild/android-x64": "0.25.12",
"@esbuild/darwin-arm64": "0.25.12",
"@esbuild/darwin-x64": "0.25.12",
"@esbuild/freebsd-arm64": "0.25.12",
"@esbuild/freebsd-x64": "0.25.12",
"@esbuild/linux-arm": "0.25.12",
"@esbuild/linux-arm64": "0.25.12",
"@esbuild/linux-ia32": "0.25.12",
"@esbuild/linux-loong64": "0.25.12",
"@esbuild/linux-mips64el": "0.25.12",
"@esbuild/linux-ppc64": "0.25.12",
"@esbuild/linux-riscv64": "0.25.12",
"@esbuild/linux-s390x": "0.25.12",
"@esbuild/linux-x64": "0.25.12",
"@esbuild/netbsd-arm64": "0.25.12",
"@esbuild/netbsd-x64": "0.25.12",
"@esbuild/openbsd-arm64": "0.25.12",
"@esbuild/openbsd-x64": "0.25.12",
"@esbuild/openharmony-arm64": "0.25.12",
"@esbuild/sunos-x64": "0.25.12",
"@esbuild/win32-arm64": "0.25.12",
"@esbuild/win32-ia32": "0.25.12",
"@esbuild/win32-x64": "0.25.12"
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"name": "tonalmine-palettes",
"private": true,
"type": "module",
"scripts": {
"build": "esbuild build_palettes.mjs --bundle --platform=node --format=esm --outfile=.build/palettes.mjs --log-level=warning && node .build/palettes.mjs"
},
"devDependencies": {
"@material/material-color-utilities": "0.4.0",
"esbuild": "^0.25.0"
}
}