9 Commits

Author SHA1 Message Date
2819c12ce0 wip(ui): play result delegate 2025-11-16 16:38:31 +08:00
a086573c0a feat(ui.theme): add arcaea colors 2025-11-15 13:54:09 +08:00
ef61ecf6ae fix(ui.theme): expose colors to qml 2025-11-15 13:20:58 +08:00
71e9f05632 impr(ui.theme): convert CustomPalette to dataclass 2025-11-15 13:20:18 +08:00
0966a3eb40 impr(ui.theme): add secondary & tertiary colors 2025-11-15 13:11:45 +08:00
a2148c7d24 chore(ui.resources): play result grade icons 2025-11-15 13:07:46 +08:00
65dab51734 fix(core): database logics 2025-11-09 00:40:23 +08:00
3679831201 wip: theme system
- Add theme id
- WIP theme cache key
- Force scheme (light/dark) for dynamic theme
- でびるんちゃんかわいい
2025-11-09 00:32:28 +08:00
7a3c186743 impr(ui): nav listview animation 2025-11-08 21:40:47 +08:00
34 changed files with 2459 additions and 1021 deletions

View File

@ -1,6 +1,7 @@
from pathlib import Path from pathlib import Path
from sqlalchemy import text from arcaea_offline.models import CalculatedPotential
from sqlalchemy import select
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from core.settings import SettingsKeys, settings from core.settings import SettingsKeys, settings
@ -17,13 +18,14 @@ class Database:
if not db_path: if not db_path:
raise ValueError("DatabaseConn is empty") raise ValueError("DatabaseConn is empty")
self.engine = create_engine(db_path_to_sqlite_url(Path(db_path))) db_path = Path(db_path)
if not db_path.exists():
raise FileNotFoundError(f"{db_path} does not exist")
self.engine = create_engine(db_path_to_sqlite_url(db_path))
self.sessionmaker = sessionmaker(bind=self.engine) self.sessionmaker = sessionmaker(bind=self.engine)
@property @property
def b30(self) -> float | None: def b30(self) -> float | None:
with self.sessionmaker() as session: with self.sessionmaker() as session:
result = session.execute( return session.scalar(select(CalculatedPotential.b30))
text("SELECT b30 FROM calculated_potential")
).fetchone()
return result[0] if result else None

View File

@ -75,6 +75,8 @@ files = [
"ui/qmls/App.qml", "ui/qmls/App.qml",
"ui/qmls/AppMain.qml", "ui/qmls/AppMain.qml",
"ui/qmls/Components/PlayResultDelegate.qml",
"ui/viewmodels/overview.py", "ui/viewmodels/overview.py",
"ui/qmls/Overview.qml", "ui/qmls/Overview.qml",

View File

@ -7,6 +7,10 @@ RowLayout {
id: layout id: layout
spacing: 5 spacing: 5
SystemPalette {
id: systemPalette
}
ListModel { ListModel {
id: navListModel id: navListModel
@ -35,6 +39,9 @@ RowLayout {
id: navListItem id: navListItem
required property int index required property int index
required property string label required property string label
property bool isActive: navListView.currentIndex === index
width: parent.width width: parent.width
height: 30 height: 30
@ -50,14 +57,29 @@ RowLayout {
anchors.fill: parent anchors.fill: parent
text: parent.label text: parent.label
color: parent.isActive ? systemPalette.highlightedText : systemPalette.text
z: 10
Behavior on color {
ColorAnimation {
duration: 150
}
} }
} }
highlight: Rectangle { Rectangle {
width: parent.width width: parent.isActive ? parent.width : 0
height: 30 Behavior on width {
color: "#FFFF88" NumberAnimation {
y: ListView.view.currentItem.y easing.type: Easing.OutQuad
duration: 200
}
}
height: parent.height
color: systemPalette.highlight
z: 1
}
} }
} }

View File

@ -0,0 +1,124 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.VectorImage
import "../libs/formatters.mjs" as Formatters
RowLayout {
id: root
required property var playResult
property alias pr: root.playResult
spacing: 8
SystemPalette {
id: systemPalette
}
component PFLLabel: RowLayout {
required property string label
required property var value
property color color: systemPalette.text
spacing: 0.5
Label {
Layout.alignment: Qt.AlignBaseline
text: parent.label
font.pointSize: 8
font.bold: true
color: parent.color
}
Label {
Layout.alignment: Qt.AlignBaseline
text: parent.value ?? '-'
color: parent.color
}
}
function getGradeIcon(gradeLabel: string): string {
const scheme = Application.styleHints.colorScheme == Qt.ColorScheme.Dark ? 'dark' : 'light';
const filenameMap = {
'EX+': 'ex-plus',
'EX': 'ex',
'AA': 'aa',
'A': 'a',
'B': 'b',
'C': 'c',
'D': 'd'
};
let filenameBase = filenameMap[gradeLabel];
if (scheme === 'dark') {
filenameBase += '-dark';
}
return `qrc:/images/grades/${filenameBase}.svg`;
}
TextMetrics {
id: gradeTextMetrics
text: 'EX+'
font.pointSize: 18
font.bold: true
}
VectorImage {
id: gradeIcon
Layout.preferredWidth: gradeTextMetrics.width
Layout.preferredHeight: gradeTextMetrics.width
fillMode: VectorImage.PreserveAspectFit
preferredRendererType: VectorImage.CurveRenderer
source: root.getGradeIcon(Formatters.scoreToGrade(root.pr.score))
}
ColumnLayout {
Layout.fillWidth: true
spacing: 1
Label {
Layout.fillWidth: true
text: root.pr.score
font.pointSize: 16
}
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 2
spacing: 5
PFLLabel {
label: 'P'
value: root.pr.pure
color: appTheme.pure
}
PFLLabel {
label: 'F'
value: root.pr.far
color: appTheme.far
}
PFLLabel {
label: 'L'
value: root.pr.lost
color: appTheme.lost
}
}
PFLLabel {
Layout.fillWidth: true
Layout.leftMargin: 2
label: 'MR'
value: root.pr.maxRecall
}
}
}

View File

@ -1,6 +1,9 @@
module Components module Components
internal SelectorBase SelectorBase.qml internal SelectorBase SelectorBase.qml
DirectorySelector 1.0 DirectorySelector.qml DirectorySelector 1.0 DirectorySelector.qml
FileSelector 1.0 FileSelector.qml FileSelector 1.0 FileSelector.qml
SectionTitle 1.0 SectionTitle.qml SectionTitle 1.0 SectionTitle.qml
PlayResultDelegate 1.0 PlayResultDelegate.qml

View File

@ -0,0 +1,18 @@
export function scoreToGrade(score) {
const gradeThresholds = [
{ minimum: 9900000, grade: "EX+" },
{ minimum: 9800000, grade: "EX" },
{ minimum: 9500000, grade: "AA" },
{ minimum: 9200000, grade: "A" },
{ minimum: 8900000, grade: "B" },
{ minimum: 8600000, grade: "C" },
];
for (const threshold of gradeThresholds) {
if (score >= threshold.minimum) {
return threshold.grade;
}
}
return "D";
}

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="a-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="9.3125"
inkscape:cy="16.015625"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,19"
orientation="14,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="3.0000001,4.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,5.0000001"
orientation="-14,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="21,20"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,9.0000002"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,20.4"
orientation="0,1"
id="guide19"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#62476c;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#ab73a3;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 10.722656,3.0996094 5.0820312,19.513672 4.7421875,20.5 h 3.171875 l 1.375,-4 h 5.4218755 l 1.374999,4 h 3.171875 L 18.917969,19.513672 13.277344,3.0996094 Z M 12,8.6113281 13.679688,13.5 h -3.359375 z"
id="path6"
inkscape:label="stroke"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);fill-opacity:1;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 11.080078,3.6000004 5.5546869,19.676172 5.4433588,20.000391 h 2.1132816 l 1.3750004,-4 h 6.1367182 l 1.375,4 h 2.113282 L 18.445312,19.676172 12.919922,3.6000004 Z M 12,7.0746098 14.380859,14.000391 H 9.6191408 Z"
id="path7"
inkscape:label="A" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="a.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="45.254834"
inkscape:cx="7.2036503"
inkscape:cy="15.280136"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,19"
orientation="14,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="3.0000001,4.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,5.0000001"
orientation="-14,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="21,20"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,9.0000002"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,20.4"
orientation="0,1"
id="guide19"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#46324d;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#92588a;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 10.722656,3.0996094 5.0820312,19.513672 4.7421875,20.5 h 3.171875 l 1.375,-4 h 5.4218755 l 1.374999,4 h 3.171875 L 18.917969,19.513672 13.277344,3.0996094 Z M 12,8.6113281 13.679688,13.5 h -3.359375 z"
id="path6"
inkscape:label="stroke"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);fill-opacity:1;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 11.080078,3.6000004 5.5546869,19.676172 5.4433588,20.000391 h 2.1132816 l 1.3750004,-4 h 6.1367182 l 1.375,4 h 2.113282 L 18.445312,19.676172 12.919922,3.6000004 Z M 12,7.0746098 14.380859,14.000391 H 9.6191408 Z"
id="path7"
inkscape:label="A" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="aa-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="9.75"
inkscape:cy="13.078125"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,19"
orientation="14,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="3.0000001,4.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,5.0000001"
orientation="-14,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="21,20"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,19"
orientation="-1,0"
id="guide7"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="10,19"
orientation="-1,0"
id="guide8"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,9.0000002"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,20.4"
orientation="0,1"
id="guide19"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#785880;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#b464a6;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<path
id="path29"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 7.2226562 3.0996094 L 1.5820312 19.513672 L 1.2421875 20.5 L 4.4140625 20.5 L 5.7890625 16.5 L 9.6171875 16.5 L 8.5820312 19.513672 L 8.2421875 20.5 L 11.414062 20.5 L 12.789062 16.5 L 18.210938 16.5 L 19.585938 20.5 L 22.757812 20.5 L 22.417969 19.513672 L 16.777344 3.0996094 L 14.222656 3.0996094 L 12 9.5683594 C 11.259181 7.4122493 10.518269 5.2556825 9.7773438 3.0996094 L 7.2226562 3.0996094 z M 8.5 8.6113281 L 10.179688 13.5 L 6.8203125 13.5 L 8.5 8.6113281 z M 15.5 8.6113281 L 17.179688 13.5 L 13.820312 13.5 L 15.5 8.6113281 z "
inkscape:label="stroke" />
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
d="M 14.580078,3.5996094 9.0546875,19.675781 8.9433594,20 h 2.1132816 l 1.375,-4 h 6.136718 l 1.375,4 h 2.113282 L 21.945312,19.675781 16.419922,3.5996094 Z M 15.5,7.0742188 17.880859,14 h -4.761718 z"
id="path30"
inkscape:label="A" />
<path
id="path23"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
d="M 7.5800781,3.5996094 2.0546875,19.675781 1.9433594,20 h 2.1132812 l 1.375,-4 h 3.8300782 l 0.6875,-2 H 6.1191406 L 8.5,7.0742188 10.414062,12.644531 11.470703,9.5683594 9.4199219,3.5996094 Z"
sodipodi:nodetypes="ccccccccccccc"
inkscape:label="A_cut" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="aa.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="9.75"
inkscape:cy="13.078125"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg1"
showguides="true">
<sodipodi:guide
position="3.0000001,19"
orientation="14,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="3.0000001,4.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,5.0000001"
orientation="-14,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="21,20"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,19"
orientation="-1,0"
id="guide7"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="10,19"
orientation="-1,0"
id="guide8"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,9.0000002"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="15.5,20.4"
orientation="0,1"
id="guide19"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#5a3463;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#9b4b8d;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<path
id="path29"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 7.2226562 3.0996094 L 1.5820312 19.513672 L 1.2421875 20.5 L 4.4140625 20.5 L 5.7890625 16.5 L 9.6171875 16.5 L 8.5820312 19.513672 L 8.2421875 20.5 L 11.414062 20.5 L 12.789062 16.5 L 18.210938 16.5 L 19.585938 20.5 L 22.757812 20.5 L 22.417969 19.513672 L 16.777344 3.0996094 L 14.222656 3.0996094 L 12 9.5683594 C 11.259181 7.4122493 10.518269 5.2556825 9.7773438 3.0996094 L 7.2226562 3.0996094 z M 8.5 8.6113281 L 10.179688 13.5 L 6.8203125 13.5 L 8.5 8.6113281 z M 15.5 8.6113281 L 17.179688 13.5 L 13.820312 13.5 L 15.5 8.6113281 z "
inkscape:label="stroke" />
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
d="M 14.580078,3.5996094 9.0546875,19.675781 8.9433594,20 h 2.1132816 l 1.375,-4 h 6.136718 l 1.375,4 h 2.113282 L 21.945312,19.675781 16.419922,3.5996094 Z M 15.5,7.0742188 17.880859,14 h -4.761718 z"
id="path30"
inkscape:label="A" />
<path
id="path23"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
d="M 7.5800781,3.5996094 2.0546875,19.675781 1.9433594,20 h 2.1132812 l 1.375,-4 h 3.8300782 l 0.6875,-2 H 6.1191406 L 8.5,7.0742188 10.414062,12.644531 11.470703,9.5683594 9.4199219,3.5996094 Z"
sodipodi:nodetypes="ccccccccccccc"
inkscape:label="A_cut" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="b-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="10.625"
inkscape:cy="13.640625"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,5.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,19"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12.5"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.0000001,20"
orientation="-1,0"
id="guide11"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="17.5,3.9996091"
orientation="-1,0"
id="guide13"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#62476c;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#ab73a3;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient1"
gradientUnits="userSpaceOnUse"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none"
transform="translate(0.375)">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 5.5,3.5 v 17 h 6.75 c 2.973855,0 5.5,-2.314875 5.5,-5.25 0,-1.721917 -0.943723,-3.16211 -2.261719,-4.113281 C 16.093694,10.320901 16.5,9.3384477 16.5,8.25 16.5,5.7007829 14.533131,3.5 12,3.5 Z m 3,3 H 12 c 0.774581,0 1.5,0.6927751 1.5,1.75 C 13.5,9.3072249 12.774581,10 12,10 H 8.5 Z m 0,6.5 h 3.25 0.25 0.25 c 1.456677,0 2.5,1.044783 2.5,2.25 0,1.205217 -1.043323,2.25 -2.5,2.25 H 8.5 Z"
id="path22"
inkscape:label="stroke"
sodipodi:nodetypes="ccsscssccssscccccssscc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient1);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 6,4 v 16 h 6.25 c 2.715802,0 5,-2.102463 5,-4.75 0,-1.719783 -0.962948,-3.210042 -2.382812,-4.042969 C 15.571798,10.433759 16,9.3833207 16,8.25 16,5.9562068 14.242331,4 12,4 Z m 2,2 h 4 c 1.071377,0 2,0.9539424 2,2.25 0,1.2960576 -0.928623,2.25 -2,2.25 H 8 Z m 4.25,6.486328 V 12.5 c 1.702476,0 3,1.255402 3,2.75 0,1.494598 -1.297524,2.75 -3,2.75 H 8 v -5.5 h 4 c 0.08442,0 0.167024,-0.0082 0.25,-0.01367 z"
id="path23"
inkscape:label="B" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="b.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="9.546875"
inkscape:cy="13.828125"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,5.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,19"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12.5"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.0000001,20"
orientation="-1,0"
id="guide11"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="17.5,3.9996091"
orientation="-1,0"
id="guide13"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#43334a;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#755b7c;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient23"
gradientUnits="userSpaceOnUse"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none"
transform="translate(0.375)">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 5.5,3.5 v 17 h 6.75 c 2.973855,0 5.5,-2.314875 5.5,-5.25 0,-1.721917 -0.943723,-3.16211 -2.261719,-4.113281 C 16.093694,10.320901 16.5,9.3384477 16.5,8.25 16.5,5.7007829 14.533131,3.5 12,3.5 Z m 3,3 H 12 c 0.774581,0 1.5,0.6927751 1.5,1.75 C 13.5,9.3072249 12.774581,10 12,10 H 8.5 Z m 0,6.5 h 3.25 0.25 0.25 c 1.456677,0 2.5,1.044783 2.5,2.25 0,1.205217 -1.043323,2.25 -2.5,2.25 H 8.5 Z"
id="path22"
inkscape:label="stroke"
sodipodi:nodetypes="ccsscssccssscccccssscc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 6,4 v 16 h 6.25 c 2.715802,0 5,-2.102463 5,-4.75 0,-1.719783 -0.962948,-3.210042 -2.382812,-4.042969 C 15.571798,10.433759 16,9.3833207 16,8.25 16,5.9562068 14.242331,4 12,4 Z m 2,2 h 4 c 1.071377,0 2,0.9539424 2,2.25 0,1.2960576 -0.928623,2.25 -2,2.25 H 8 Z m 4.25,6.486328 V 12.5 c 1.702476,0 3,1.255402 3,2.75 0,1.494598 -1.297524,2.75 -3,2.75 H 8 v -5.5 h 4 c 0.08442,0 0.167024,-0.0082 0.25,-0.01367 z"
id="path23"
inkscape:label="B" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="c-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="22.627417"
inkscape:cx="8.8167377"
inkscape:cy="14.164233"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12"
orientation="0.60181502,0.79863551"
id="guide6"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12"
orientation="-0.60181502,0.79863551"
id="guide7"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient9"
inkscape:collect="always">
<stop
style="stop-color:#5c433d;stop-opacity:1;"
offset="0"
id="stop9" />
<stop
style="stop-color:#9d6c85;stop-opacity:1;"
offset="1"
id="stop10" />
</linearGradient>
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#46324d;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#92588a;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient9"
id="linearGradient10"
x1="12"
y1="3.5163455"
x2="12"
y2="20.483654"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 12.5,3 C 7.7659933,3 4,7.0984281 4,12 c 0,4.901572 3.7659934,9 8.5,9 2.899254,0 5.460617,-1.548293 6.980469,-3.859375 l 0.27539,-0.417969 -2.507812,-1.65039 -0.275391,0.417968 C 15.95623,17.035809 14.33618,18 12.5,18 9.5033162,18 7,15.389122 7,12 7,8.6108784 9.5033164,6 12.5,6 c 1.836197,0 3.456233,0.9641762 4.472656,2.5097656 L 17.248047,8.9277344 19.755859,7.2773437 19.480469,6.859375 C 17.96062,4.5482711 15.399265,3 12.5,3 Z"
id="path8"
inkscape:label="stroke"
sodipodi:nodetypes="sssccccsssccccs" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient10);stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 12.5,3.5 c -4.4450362,0 -8,3.8534536 -8,8.5 0,4.646547 3.5549639,8.5 8,8.5 2.721277,0 5.127673,-1.452972 6.5625,-3.634766 L 17.390625,15.765625 C 16.291564,17.436853 14.512289,18.5 12.5,18.5 c -3.2869503,0 -6,-2.862275 -6,-6.5 0,-3.6377248 2.7130498,-6.5 6,-6.5 2.012302,0 3.791567,1.0631305 4.890625,2.734375 L 19.0625,7.1347656 C 17.627676,4.9529509 15.221292,3.5 12.5,3.5 Z"
id="path9"
inkscape:label="C" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="c.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="9.828125"
inkscape:cy="12.421875"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12"
orientation="0.60181502,0.79863551"
id="guide6"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12"
orientation="-0.60181502,0.79863551"
id="guide7"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient9"
inkscape:collect="always">
<stop
style="stop-color:#3b2b27;stop-opacity:1;"
offset="0"
id="stop9" />
<stop
style="stop-color:#80566b;stop-opacity:1;"
offset="1"
id="stop10" />
</linearGradient>
<linearGradient
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#46324d;stop-opacity:1;"
offset="0"
id="stop34" />
<stop
style="stop-color:#92588a;stop-opacity:1;"
offset="1"
id="stop35" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="11.470703"
y1="3.5996094"
x2="11.470703"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient9"
id="linearGradient10"
x1="12"
y1="3.5163455"
x2="12"
y2="20.483654"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 12.5,3 C 7.7659933,3 4,7.0984281 4,12 c 0,4.901572 3.7659934,9 8.5,9 2.899254,0 5.460617,-1.548293 6.980469,-3.859375 l 0.27539,-0.417969 -2.507812,-1.65039 -0.275391,0.417968 C 15.95623,17.035809 14.33618,18 12.5,18 9.5033162,18 7,15.389122 7,12 7,8.6108784 9.5033164,6 12.5,6 c 1.836197,0 3.456233,0.9641762 4.472656,2.5097656 L 17.248047,8.9277344 19.755859,7.2773437 19.480469,6.859375 C 17.96062,4.5482711 15.399265,3 12.5,3 Z"
id="path8"
inkscape:label="stroke"
sodipodi:nodetypes="sssccccsssccccs" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient10);stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 12.5,3.5 c -4.4450362,0 -8,3.8534536 -8,8.5 0,4.646547 3.5549639,8.5 8,8.5 2.721277,0 5.127673,-1.452972 6.5625,-3.634766 L 17.390625,15.765625 C 16.291564,17.436853 14.512289,18.5 12.5,18.5 c -3.2869503,0 -6,-2.862275 -6,-6.5 0,-3.6377248 2.7130498,-6.5 6,-6.5 2.012302,0 3.791567,1.0631305 4.890625,2.734375 L 19.0625,7.1347656 C 17.627676,4.9529509 15.221292,3.5 12.5,3.5 Z"
id="path9"
inkscape:label="C" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="d-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="12.1875"
inkscape:cy="12.109375"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,5.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,19"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12.5"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.0000001,20"
orientation="-1,0"
id="guide11"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="17.5,3.9996091"
orientation="-1,0"
id="guide13"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient8"
inkscape:collect="always">
<stop
style="stop-color:#842a4b;stop-opacity:1;"
offset="0"
id="stop7" />
<stop
style="stop-color:#bd516c;stop-opacity:1;"
offset="1"
id="stop8" />
</linearGradient>
<linearGradient
id="linearGradient5"
inkscape:collect="always">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8"
id="linearGradient6"
x1="11"
y1="4"
x2="11"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient5);fill-opacity:1;stroke:none"
transform="translate(0.375)">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 5.5,3.5 v 17 H 11 c 4.462075,0 8,-3.878181 8,-8.5 0,-4.621819 -3.537925,-8.5 -8,-8.5 z m 3,3 H 11 c 2.715731,0 5,2.3827768 5,5.5 0,3.117223 -2.284269,5.5 -5,5.5 H 8.5 Z"
id="path4"
inkscape:label="stroke"
sodipodi:nodetypes="ccsssccssscc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient6);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 6,4 v 16 h 5 c 4.171863,0 7.5,-3.63217 7.5,-8 0,-4.3678304 -3.328137,-8 -7.5,-8 z m 2,2 h 3 c 3.007839,0 5.5,2.635844 5.5,6 0,3.364156 -2.492161,6 -5.5,6 H 8 Z"
id="path5"
inkscape:label="D" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="d.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="12.1875"
inkscape:cy="12.109375"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,5.0000001"
orientation="0,1"
id="guide2"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="21,19"
orientation="0,1"
id="guide4"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,12.5"
orientation="0,1"
id="guide10"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="12,20"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.0000001,20"
orientation="-1,0"
id="guide11"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="17.5,3.9996091"
orientation="-1,0"
id="guide13"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1">
<linearGradient
id="linearGradient8"
inkscape:collect="always">
<stop
style="stop-color:#5d1d35;stop-opacity:1;"
offset="0"
id="stop7" />
<stop
style="stop-color:#9f3c55;stop-opacity:1;"
offset="1"
id="stop8" />
</linearGradient>
<linearGradient
id="linearGradient5"
inkscape:collect="always">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8"
id="linearGradient6"
x1="11"
y1="4"
x2="11"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient5);fill-opacity:1;stroke:none"
transform="translate(0.375)">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 5.5,3.5 v 17 H 11 c 4.462075,0 8,-3.878181 8,-8.5 0,-4.621819 -3.537925,-8.5 -8,-8.5 z m 3,3 H 11 c 2.715731,0 5,2.3827768 5,5.5 0,3.117223 -2.284269,5.5 -5,5.5 H 8.5 Z"
id="path4"
inkscape:label="stroke"
sodipodi:nodetypes="ccsssccssscc" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient6);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 6,4 v 16 h 5 c 4.171863,0 7.5,-3.63217 7.5,-8 0,-4.3678304 -3.328137,-8 -7.5,-8 z m 2,2 h 3 c 3.007839,0 5.5,2.635844 5.5,6 0,3.364156 -2.492161,6 -5.5,6 H 8 Z"
id="path5"
inkscape:label="D" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="ex-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="22.627418"
inkscape:cx="14.429397"
inkscape:cy="14.517786"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true" />
<defs
id="defs1">
<linearGradient
id="linearGradient3"
inkscape:collect="always">
<stop
style="stop-color:#ba2cae;stop-opacity:1;"
offset="0"
id="stop2" />
<stop
style="stop-color:#397fc6;stop-opacity:1;"
offset="1"
id="stop3" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect13"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="spiro"
id="path-effect11"
is_visible="true"
lpeversion="1" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3"
id="linearGradient23"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3"
id="linearGradient1"
gradientUnits="userSpaceOnUse"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientTransform="translate(3.030469)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3"
id="linearGradient2"
gradientUnits="userSpaceOnUse"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientTransform="translate(3.030469)" />
</defs>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="strokes">
<path
id="path6"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
inkscape:label="EX-stroke"
d="m 2.8,3.5 v 17 h 7.714844 1.285157 2.02539 L 16.133984,15.548828 18.442578,20.5 h 3.310547 L 17.78828,12 21.753125,3.5 H 18.442578 L 16.133984,8.4511719 13.825391,3.5 h -2.02539 -1.285157 z m 3,3 h 6.113281 l 2.566407,5.5 -2.566407,5.5 H 5.8 v -4 h 5 v -3 h -5 z" />
</g>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient1);stroke:#cccccc;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 3.3,4 v 16 h 8.000001 V 18 H 5.3 v -5 h 5.000001 V 11 H 5.3 V 6 h 6.000001 V 4 Z"
id="path7"
inkscape:label="E" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient2);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 11.3,4.0000008 15.030469,12 11.3,20 h 2.207031 L 16.133984,14.367188 18.760937,20 h 2.207031 L 17.2375,12 20.967968,4.0000008 H 18.760937 L 16.133984,9.6328133 13.507031,4.0000008 Z"
id="path4"
inkscape:label="X" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="0.755 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="exPlus-dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="181.01934"
inkscape:cx="9.7890095"
inkscape:cy="6.7534222"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer2"
showguides="true" />
<defs
id="defs1">
<linearGradient
id="linearGradient22"
inkscape:collect="always">
<stop
style="stop-color:#bf33cc;stop-opacity:1;"
offset="0"
id="stop22" />
<stop
style="stop-color:#4791d1;stop-opacity:1;"
offset="1"
id="stop23" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect13"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="spiro"
id="path-effect11"
is_visible="true"
lpeversion="1" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient22"
id="linearGradient23"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="strokes">
<path
id="path6"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
inkscape:label="EX-stroke"
d="M -0.230469 3.5 L -0.230469 20.5 L 7.4843747 20.5 L 8.769531 20.5 L 10.794922 20.5 L 13.103515 15.548828 L 15.412109 20.5 L 18.722656 20.5 L 14.757812 12 L 18.722656 3.5 L 15.412109 3.5 L 13.103515 8.4511719 L 10.794922 3.5 L 8.769531 3.5 L 7.4843747 3.5 L -0.230469 3.5 z M 2.769531 6.5 L 8.8828123 6.5 L 11.449219 12 L 8.8828123 17.5 L 2.769531 17.5 L 2.769531 13.5 L 7.769531 13.5 L 7.769531 10.5 L 2.769531 10.5 L 2.769531 6.5 z "
transform="translate(0.980469)" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 17.769531,8.5 v 2 h -2 v 3 h 2 v 2 h 3 v -2 h 2 v -3 h -2 v -2 z"
id="path1"
inkscape:label="plus-stroke"
transform="translate(0.980469)"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 0.269531,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
id="path7"
inkscape:label="E"
transform="translate(0.980469)" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 8.2695306,4.0000008 12,12 8.2695306,20 H 10.476562 L 13.103515,14.367188 15.730468,20 H 17.9375 L 14.207031,12 17.9375,4.0000008 H 15.730468 L 13.103515,9.6328133 10.476562,4.0000008 Z"
id="path4"
inkscape:label="X"
transform="translate(0.980469)" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 18.269531,9 v 1 1 h -1 -1 v 2 h 1 1 v 1 1 h 2 v -1 -1 h 1 1 v -2 h -1 -1 V 10 9 Z"
id="path2"
inkscape:label="plus"
transform="translate(0.980469)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="0.755 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="ex-plus.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32"
inkscape:cx="2.75"
inkscape:cy="12.296875"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer3"
showguides="true" />
<defs
id="defs1">
<linearGradient
id="linearGradient22"
inkscape:collect="always">
<stop
style="stop-color:#83238c;stop-opacity:1;"
offset="0"
id="stop22" />
<stop
style="stop-color:#2c72ae;stop-opacity:1;"
offset="1"
id="stop23" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect13"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="spiro"
id="path-effect11"
is_visible="true"
lpeversion="1" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient22"
id="linearGradient23"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="strokes">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 17.769531,8.5 v 2 h -2 v 3 h 2 v 2 h 3 v -2 h 2 v -3 h -2 v -2 z"
id="path26"
inkscape:label="plus-stroke"
transform="translate(0.980469)"
sodipodi:nodetypes="ccccccccccccc" />
<path
id="path28"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
inkscape:label="X-stroke"
d="m -0.230469,3.5 v 17 H 7.4843747 8.769531 10.794922 L 13.103515,15.548828 15.412109,20.5 h 3.310547 L 14.757812,12 18.722656,3.5 H 15.412109 L 13.103515,8.4511719 10.794922,3.5 H 8.769531 7.4843747 Z m 3,3 H 8.8828123 L 11.449219,12 8.8828123,17.5 H 2.769531 v -4 h 5 v -3 h -5 z"
transform="translate(0.980469)" />
</g>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 0.269531,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
id="path31"
inkscape:label="E"
transform="translate(0.980469)" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 8.2695306,4.0000008 12,12 8.2695306,20 H 10.476562 L 13.103515,14.367188 15.730468,20 H 17.9375 L 14.207031,12 17.9375,4.0000008 H 15.730468 L 13.103515,9.6328133 10.476562,4.0000008 Z"
id="path29"
inkscape:label="X"
transform="translate(0.980469)" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 18.269531,9 v 1 1 h -1 -1 v 2 h 1 1 v 1 1 h 2 v -1 -1 h 1 1 v -2 h -1 -1 V 10 9 Z"
id="path27"
inkscape:label="plus"
transform="translate(0.980469)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="ex.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="32.791667"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true" />
<defs
id="defs1">
<linearGradient
id="linearGradient22"
inkscape:collect="always">
<stop
style="stop-color:#721b6b;stop-opacity:1;"
offset="0"
id="stop22" />
<stop
style="stop-color:#295b8d;stop-opacity:1;"
offset="1"
id="stop23" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect13"
is_visible="true"
lpeversion="1" />
<inkscape:path-effect
effect="spiro"
id="path-effect11"
is_visible="true"
lpeversion="1" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient22"
id="linearGradient23"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient22"
id="linearGradient1"
gradientUnits="userSpaceOnUse"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientTransform="translate(3.130469)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient22"
id="linearGradient2"
gradientUnits="userSpaceOnUse"
x1="10.353516"
y1="4.000001"
x2="17.814453"
y2="20"
gradientTransform="translate(3.130469)" />
</defs>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="strokes">
<path
id="path28"
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
inkscape:label="X-stroke"
d="m 2.9,3.5 v 17 H 10.614844 11.9 13.925391 L 16.233984,15.548828 18.542578,20.5 h 3.310547 L 17.888281,12 21.853125,3.5 H 18.542578 L 16.233984,8.4511719 13.925391,3.5 H 11.9 10.614844 Z m 3,3 h 6.113281 l 2.566407,5.5 -2.566407,5.5 H 5.9 v -4 h 5 v -3 h -5 z" />
</g>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient1);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 3.4,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
id="path31"
inkscape:label="E" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient2);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 11.4,4.0000008 15.130469,12 11.4,20 h 2.207031 L 16.233984,14.367188 18.860937,20 h 2.207032 L 17.3375,12 21.067969,4.0000008 H 18.860937 L 16.233984,9.6328133 13.607031,4.0000008 Z"
id="path29"
inkscape:label="X" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="1 1 23 23"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
sodipodi:docname="grade-template.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="false"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
labelstyle="default"
inkscape:clip-to-page="false"
inkscape:zoom="22.435241"
inkscape:cx="10.563738"
inkscape:cy="11.789488"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<sodipodi:guide
position="3.0000001,19"
orientation="14,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="3.0000001,5.0000001"
orientation="0,18"
id="guide2"
inkscape:locked="false" />
<sodipodi:guide
position="21,5.0000001"
orientation="-14,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="21,19"
orientation="0,-18"
id="guide4"
inkscape:locked="false" />
<sodipodi:guide
position="12,12.000001"
orientation="-1,0"
id="guide5"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="图层 1"
inkscape:groupmode="layer"
id="layer1" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Grade Previews</title>
<meta charset="utf-8" />
<style>
.display {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
img {
width: 150px;
height: 150px;
}
</style>
</head>
<body>
<div id="main" style="display: flex; flex-direction: column">
<div class="gallery">
<img src="./ex-plus.svg" />
<img src="./ex.svg" />
<img src="./aa.svg" />
<img src="./a.svg" />
<img src="./b.svg" />
<img src="./c.svg" />
<img src="./d.svg" />
</div>
<div class="gallery" style="background-color: #282828">
<img src="./ex-plus-dark.svg" />
<img src="./ex-dark.svg" />
<img src="./aa-dark.svg" />
<img src="./a-dark.svg" />
<img src="./b-dark.svg" />
<img src="./c-dark.svg" />
<img src="./d-dark.svg" />
</div>
</div>
</body>
</html>

View File

@ -10,6 +10,20 @@
<file>images/icon.png</file> <file>images/icon.png</file>
<file>images/logo.png</file> <file>images/logo.png</file>
<file>images/grades/ex-plus.svg</file>
<file>images/grades/ex-plus-dark.svg</file>
<file>images/grades/ex.svg</file>
<file>images/grades/ex-dark.svg</file>
<file>images/grades/aa.svg</file>
<file>images/grades/aa-dark.svg</file>
<file>images/grades/a.svg</file>
<file>images/grades/a-dark.svg</file>
<file>images/grades/b.svg</file>
<file>images/grades/b-dark.svg</file>
<file>images/grades/c.svg</file>
<file>images/grades/c-dark.svg</file>
<file>images/grades/d.svg</file>
<file>images/grades/d-dark.svg</file>
<file>images/jacket-placeholder.png</file> <file>images/jacket-placeholder.png</file>
<file>images/stepCalculator/stamina.png</file> <file>images/stepCalculator/stamina.png</file>
<file>images/stepCalculator/play.png</file> <file>images/stepCalculator/play.png</file>
@ -20,7 +34,9 @@
<file>lang/zh_CN.qm</file> <file>lang/zh_CN.qm</file>
<file>lang/en_US.qm</file> <file>lang/en_US.qm</file>
<file>themes/default.json</file> <file>themes/m3-dynamic_default.json</file>
<file>themes/tempest.json</file> <file>themes/m3-dynamic_tempest.json</file>
<file>themes/m3-dynamic_devilun.json</file>
<file>themes/m3-dynamic_kupya.json</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,427 +0,0 @@
{
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%234E486C&custom%3ASuccess=%2300C555&colorMatch=true",
"name": "default",
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
"seed": "#4E486C",
"coreColors": {
"primary": "#4E486C"
},
"extendedColors": [
{
"name": "Success",
"color": "#00C555",
"description": "",
"harmonized": true
}
],
"schemes": {
"light": {
"primary": "#373154",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#C0B8E3",
"secondary": "#605C6C",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#E3DDF0",
"onSecondaryContainer": "#646071",
"tertiary": "#4F2B40",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#E3B0CA",
"error": "#BA1A1A",
"onError": "#FFFFFF",
"errorContainer": "#FFDAD6",
"onErrorContainer": "#93000A",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#1C1B1E",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#48464D",
"outline": "#79767E",
"outlineVariant": "#C9C5CE",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#F4EFF3",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#1C1738",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#484266",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#1C1A27",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#484554",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#301024",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#613B51",
"surfaceDim": "#DDD9DC",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F7F2F6",
"surfaceContainer": "#F1ECF0",
"surfaceContainerHigh": "#EBE7EA",
"surfaceContainerHighest": "#E6E1E5"
},
"light-medium-contrast": {
"primary": "#373154",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#EDE7FF",
"secondary": "#373443",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#6F6B7B",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#4E2B40",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#FFE4EF",
"error": "#740006",
"onError": "#FFFFFF",
"errorContainer": "#CF2C27",
"onErrorContainer": "#FFFFFF",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#121113",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#37353D",
"outline": "#545159",
"outlineVariant": "#6F6C74",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#F4EFF3",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#6F688E",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#565075",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#6F6B7B",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#565363",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#8B6078",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#71495F",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#C9C5C9",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F7F2F6",
"surfaceContainer": "#EBE7EA",
"surfaceContainerHigh": "#E0DCDF",
"surfaceContainerHighest": "#D4D0D4"
},
"light-high-contrast": {
"primary": "#2D2749",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4A4468",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#2D2A39",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#4A4757",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#432135",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#643D53",
"onTertiaryContainer": "#FFFFFF",
"error": "#600004",
"onError": "#FFFFFF",
"errorContainer": "#98000A",
"onErrorContainer": "#FFFFFF",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#000000",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#000000",
"outline": "#2D2B32",
"outlineVariant": "#4A4850",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#FFFFFF",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#4A4468",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#342E50",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#4A4757",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#34313F",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#643D53",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#4A273C",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#BBB8BB",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F4EFF3",
"surfaceContainer": "#E6E1E5",
"surfaceContainerHigh": "#D7D3D7",
"surfaceContainerHighest": "#C9C5C9"
},
"dark": {
"primary": "#C9C1EC",
"surfaceTint": "#C9C1EC",
"onPrimary": "#312C4E",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#C0B8E3",
"secondary": "#C9C4D7",
"onSecondary": "#312E3D",
"secondaryContainer": "#4A4757",
"onSecondaryContainer": "#BBB6C8",
"tertiary": "#ECB8D2",
"onTertiary": "#48253A",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#E3B0CA",
"error": "#FFB4AB",
"onError": "#690005",
"errorContainer": "#93000A",
"onErrorContainer": "#FFDAD6",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#E6E1E5",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#C9C5CE",
"outline": "#938F98",
"outlineVariant": "#48464D",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#313033",
"inversePrimary": "#605A7F",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#1C1738",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#484266",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#1C1A27",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#484554",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#301024",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#613B51",
"surfaceDim": "#141315",
"surfaceBright": "#3A393B",
"surfaceContainerLowest": "#0F0E10",
"surfaceContainerLow": "#1C1B1E",
"surfaceContainer": "#201F22",
"surfaceContainerHigh": "#2B292C",
"surfaceContainerHighest": "#363437"
},
"dark-medium-contrast": {
"primary": "#E0D7FF",
"surfaceTint": "#C9C1EC",
"onPrimary": "#262142",
"primaryContainer": "#938CB4",
"onPrimaryContainer": "#000000",
"secondary": "#DFD9ED",
"onSecondary": "#262432",
"secondaryContainer": "#938EA0",
"onSecondaryContainer": "#000000",
"tertiary": "#FFCFE7",
"onTertiary": "#3C1A2E",
"tertiaryContainer": "#B2839C",
"onTertiaryContainer": "#000000",
"error": "#FFD2CC",
"onError": "#540003",
"errorContainer": "#FF5449",
"onErrorContainer": "#000000",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#FFFFFF",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#DFDAE4",
"outline": "#B4B0BA",
"outlineVariant": "#928F98",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#2B292C",
"inversePrimary": "#494367",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#120C2D",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#373254",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#120F1D",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#373443",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#230619",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#4E2B40",
"surfaceDim": "#141315",
"surfaceBright": "#454447",
"surfaceContainerLowest": "#080709",
"surfaceContainerLow": "#1E1D20",
"surfaceContainer": "#28272A",
"surfaceContainerHigh": "#333235",
"surfaceContainerHighest": "#3F3D40"
},
"dark-high-contrast": {
"primary": "#F3EDFF",
"surfaceTint": "#C9C1EC",
"onPrimary": "#000000",
"primaryContainer": "#C6BDE8",
"onPrimaryContainer": "#0B0627",
"secondary": "#F3EDFF",
"onSecondary": "#000000",
"secondaryContainer": "#C5C0D3",
"onSecondaryContainer": "#0C0916",
"tertiary": "#FFEBF3",
"onTertiary": "#000000",
"tertiaryContainer": "#E7B4CE",
"onTertiaryContainer": "#1C0213",
"error": "#FFECE9",
"onError": "#000000",
"errorContainer": "#FFAEA4",
"onErrorContainer": "#220001",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#FFFFFF",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#FFFFFF",
"outline": "#F3EEF8",
"outlineVariant": "#C5C1CA",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#000000",
"inversePrimary": "#494367",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#000000",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#120C2D",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#000000",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#120F1D",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#000000",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#230619",
"surfaceDim": "#141315",
"surfaceBright": "#514F52",
"surfaceContainerLowest": "#000000",
"surfaceContainerLow": "#201F22",
"surfaceContainer": "#313033",
"surfaceContainerHigh": "#3C3B3E",
"surfaceContainerHighest": "#484649"
}
},
"palettes": {
"primary": {
"0": "#000000",
"5": "#110B2C",
"10": "#1C1738",
"15": "#272142",
"20": "#312C4E",
"25": "#3C375A",
"30": "#484266",
"35": "#544E72",
"40": "#605A7F",
"50": "#797299",
"60": "#938CB4",
"70": "#AEA6CF",
"80": "#C9C1EC",
"90": "#E6DEFF",
"95": "#F4EEFF",
"98": "#FDF8FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"secondary": {
"0": "#000000",
"5": "#111018",
"10": "#1C1A23",
"15": "#26252D",
"20": "#312F38",
"25": "#3C3A43",
"30": "#48454F",
"35": "#54515B",
"40": "#605D67",
"50": "#797580",
"60": "#938F9A",
"70": "#AEA9B5",
"80": "#C9C4D0",
"90": "#E6E0EC",
"95": "#F4EEFB",
"98": "#FDF8FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"tertiary": {
"0": "#000000",
"5": "#1B0C13",
"10": "#27171E",
"15": "#322128",
"20": "#3E2B33",
"25": "#49363E",
"30": "#564149",
"35": "#624D55",
"40": "#6F5861",
"50": "#897179",
"60": "#A38A93",
"70": "#BFA4AD",
"80": "#DCBFC9",
"90": "#F9DBE5",
"95": "#FFECF1",
"98": "#FFF8F8",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral": {
"0": "#000000",
"5": "#111112",
"10": "#1C1B1D",
"15": "#262527",
"20": "#313032",
"25": "#3C3B3D",
"30": "#484648",
"35": "#545254",
"40": "#605E60",
"50": "#797678",
"60": "#939092",
"70": "#ADAAAC",
"80": "#C9C5C7",
"90": "#E5E1E3",
"95": "#F4EFF1",
"98": "#FDF8FA",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral-variant": {
"0": "#000000",
"5": "#111014",
"10": "#1C1B1F",
"15": "#262529",
"20": "#313034",
"25": "#3C3B3F",
"30": "#48464A",
"35": "#545256",
"40": "#605D62",
"50": "#79767B",
"60": "#939094",
"70": "#AEAAAF",
"80": "#C9C5CA",
"90": "#E6E1E6",
"95": "#F4EFF4",
"98": "#FDF8FD",
"99": "#FFFBFF",
"100": "#FFFFFF"
}
}
}

View File

@ -0,0 +1,8 @@
{
"id": "default",
"name": "Default",
"colors": {
"primary": "#4e486c"
},
"options": {}
}

View File

@ -0,0 +1,11 @@
{
"id": "devilun",
"name": "Devilun",
"colors": {
"primary": "#d1779d",
"secondary": "#85a9a5"
},
"options": {
"forceScheme": "dark"
}
}

View File

@ -0,0 +1,10 @@
{
"id": "kupya",
"name": "Kupya",
"colors": {
"primary": "#ffeb9e"
},
"options": {
"forceScheme": "light"
}
}

View File

@ -0,0 +1,8 @@
{
"id": "tempest",
"name": "Tempest",
"colors": {
"primary": "#186d98"
},
"options": {}
}

View File

@ -1,427 +0,0 @@
{
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%23186D98&custom%3ASuccess=%2300C555&colorMatch=true",
"name": "tempest",
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
"seed": "#186D98",
"coreColors": {
"primary": "#186D98"
},
"extendedColors": [
{
"name": "Success",
"color": "#00C555",
"description": "",
"harmonized": true
}
],
"schemes": {
"light": {
"primary": "#005479",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#CEE9FF",
"secondary": "#496173",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#C9E3F8",
"onSecondaryContainer": "#4D6678",
"tertiary": "#683D7A",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#F9DCFF",
"error": "#BA1A1A",
"onError": "#FFFFFF",
"errorContainer": "#FFDAD6",
"onErrorContainer": "#93000A",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#191C1F",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#40484E",
"outline": "#70787F",
"outlineVariant": "#C0C7CF",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#EFF1F5",
"inversePrimary": "#88CEFE",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#001E2E",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#004C6D",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#021E2D",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#314A5B",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#300443",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#603572",
"surfaceDim": "#D8DADE",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F2F4F7",
"surfaceContainer": "#ECEEF2",
"surfaceContainerHigh": "#E6E8EC",
"surfaceContainerHighest": "#E0E2E6"
},
"light-medium-contrast": {
"primary": "#003A55",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#203949",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#577082",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#4E2460",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#FFFFFF",
"error": "#740006",
"onError": "#FFFFFF",
"errorContainer": "#CF2C27",
"onErrorContainer": "#FFFFFF",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#0E1214",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#2F373D",
"outline": "#4C535A",
"outlineVariant": "#666E75",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#EFF1F5",
"inversePrimary": "#88CEFE",
"primaryFixed": "#23749F",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#005B82",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#577082",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#3F5869",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#895C9B",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#6F4381",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#C4C7CA",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F2F4F7",
"surfaceContainer": "#E6E8EC",
"surfaceContainerHigh": "#DBDDE1",
"surfaceContainerHighest": "#CFD2D5"
},
"light-high-contrast": {
"primary": "#003046",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#004E71",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#152F3F",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#344C5D",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#421955",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#623874",
"onTertiaryContainer": "#FFFFFF",
"error": "#600004",
"onError": "#FFFFFF",
"errorContainer": "#98000A",
"onErrorContainer": "#FFFFFF",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#000000",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#000000",
"outline": "#252D33",
"outlineVariant": "#424A51",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#FFFFFF",
"inversePrimary": "#88CEFE",
"primaryFixed": "#004E71",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#003650",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#344C5D",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#1C3546",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#623874",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#4A205C",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#B6B9BD",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#EFF1F5",
"surfaceContainer": "#E0E2E6",
"surfaceContainerHigh": "#D2D4D8",
"surfaceContainerHighest": "#C4C7CA"
},
"dark": {
"primary": "#88CEFE",
"surfaceTint": "#88CEFE",
"onPrimary": "#00344D",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#CEE9FF",
"secondary": "#B0CADF",
"onSecondary": "#1A3343",
"secondaryContainer": "#314A5B",
"onSecondaryContainer": "#9FB8CD",
"tertiary": "#E8B4FA",
"onTertiary": "#471E59",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#F9DCFF",
"error": "#FFB4AB",
"onError": "#690005",
"errorContainer": "#93000A",
"onErrorContainer": "#FFDAD6",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#E0E2E6",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#C0C7CF",
"outline": "#8A9299",
"outlineVariant": "#40484E",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#2D3134",
"inversePrimary": "#03658F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#001E2E",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#004C6D",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#021E2D",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#314A5B",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#300443",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#603572",
"surfaceDim": "#101417",
"surfaceBright": "#363A3D",
"surfaceContainerLowest": "#0B0F11",
"surfaceContainerLow": "#191C1F",
"surfaceContainer": "#1D2023",
"surfaceContainerHigh": "#272A2D",
"surfaceContainerHighest": "#323538"
},
"dark-medium-contrast": {
"primary": "#BBE1FF",
"surfaceTint": "#88CEFE",
"onPrimary": "#00293D",
"primaryContainer": "#5098C5",
"onPrimaryContainer": "#000000",
"secondary": "#C6E0F5",
"onSecondary": "#0D2838",
"secondaryContainer": "#7B94A7",
"onSecondaryContainer": "#000000",
"tertiary": "#F5D0FF",
"onTertiary": "#3B114E",
"tertiaryContainer": "#AF7FC1",
"onTertiaryContainer": "#000000",
"error": "#FFD2CC",
"onError": "#540003",
"errorContainer": "#FF5449",
"onErrorContainer": "#000000",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#FFFFFF",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#D6DDE5",
"outline": "#ABB3BB",
"outlineVariant": "#899199",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#272A2D",
"inversePrimary": "#004D6F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#00131F",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#003A55",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#00131F",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#203949",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#220032",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#4E2460",
"surfaceDim": "#101417",
"surfaceBright": "#424548",
"surfaceContainerLowest": "#05080A",
"surfaceContainerLow": "#1B1E21",
"surfaceContainer": "#25282B",
"surfaceContainerHigh": "#303336",
"surfaceContainerHighest": "#3B3E41"
},
"dark-high-contrast": {
"primary": "#E3F2FF",
"surfaceTint": "#88CEFE",
"onPrimary": "#000000",
"primaryContainer": "#84CAFA",
"onPrimaryContainer": "#000D17",
"secondary": "#E3F2FF",
"onSecondary": "#000000",
"secondaryContainer": "#ACC6DB",
"onSecondaryContainer": "#000D17",
"tertiary": "#FDEAFF",
"onTertiary": "#000000",
"tertiaryContainer": "#E4B0F6",
"onTertiaryContainer": "#190026",
"error": "#FFECE9",
"onError": "#000000",
"errorContainer": "#FFAEA4",
"onErrorContainer": "#220001",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#FFFFFF",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#FFFFFF",
"outline": "#E9F1F9",
"outlineVariant": "#BCC3CB",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#000000",
"inversePrimary": "#004D6F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#000000",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#00131F",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#000000",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#00131F",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#000000",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#220032",
"surfaceDim": "#101417",
"surfaceBright": "#4D5054",
"surfaceContainerLowest": "#000000",
"surfaceContainerLow": "#1D2023",
"surfaceContainer": "#2D3134",
"surfaceContainerHigh": "#383C3F",
"surfaceContainerHighest": "#44474A"
}
},
"palettes": {
"primary": {
"0": "#000000",
"5": "#00131F",
"10": "#001E2E",
"15": "#00293D",
"20": "#00344D",
"25": "#00405D",
"30": "#004C6D",
"35": "#00587E",
"40": "#03658F",
"50": "#317EAA",
"60": "#5098C5",
"70": "#6CB3E1",
"80": "#88CEFE",
"90": "#C8E6FF",
"95": "#E5F2FF",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
},
"secondary": {
"0": "#000000",
"5": "#05121B",
"10": "#0F1D26",
"15": "#1A2731",
"20": "#25323C",
"25": "#303D47",
"30": "#3B4853",
"35": "#46545F",
"40": "#52606B",
"50": "#6B7984",
"60": "#84929E",
"70": "#9FADB9",
"80": "#BAC8D5",
"90": "#D6E4F2",
"95": "#E5F2FF",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
},
"tertiary": {
"0": "#000000",
"5": "#140D25",
"10": "#1E1730",
"15": "#29223B",
"20": "#342C46",
"25": "#3F3752",
"30": "#4B425E",
"35": "#564E6A",
"40": "#635A76",
"50": "#7C7290",
"60": "#968CAB",
"70": "#B1A6C6",
"80": "#CDC1E2",
"90": "#E9DDFF",
"95": "#F6EDFF",
"98": "#FEF7FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral": {
"0": "#000000",
"5": "#0F1113",
"10": "#1A1C1E",
"15": "#242628",
"20": "#2F3132",
"25": "#3A3B3D",
"30": "#454749",
"35": "#515254",
"40": "#5D5E60",
"50": "#767779",
"60": "#909193",
"70": "#AAABAD",
"80": "#C6C6C8",
"90": "#E2E2E4",
"95": "#F1F0F3",
"98": "#F9F9FB",
"99": "#FCFCFE",
"100": "#FFFFFF"
},
"neutral-variant": {
"0": "#000000",
"5": "#0C1215",
"10": "#171C20",
"15": "#21262B",
"20": "#2C3135",
"25": "#373C40",
"30": "#42474C",
"35": "#4E5358",
"40": "#5A5F64",
"50": "#73787D",
"60": "#8C9196",
"70": "#A7ACB1",
"80": "#C2C7CC",
"90": "#DEE3E8",
"95": "#EDF1F7",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
}
}
}

View File

@ -4,17 +4,17 @@ from typing import overload
import structlog import structlog
from PySide6.QtCore import Property, QObject, QResource, Qt, Signal from PySide6.QtCore import Property, QObject, QResource, Qt, Signal
from PySide6.QtGui import QColor, QGuiApplication, QPalette from PySide6.QtGui import QGuiApplication, QPalette
from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl
from .qml import ThemeQmlExposer from .qml import ThemeQmlExposer
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme from .shared import CustomPalette, ThemeImpl, ThemeInfo, TThemeInfoCacheKey, _TScheme
QML_IMPORT_NAME = "internal.ui.theme" QML_IMPORT_NAME = "internal.ui.theme"
QML_IMPORT_MAJOR_VERSION = 1 QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0 QML_IMPORT_MINOR_VERSION = 0
_THEME_CACHES: dict[ThemeInfo, ThemeImpl] = {} _THEME_CACHES: dict[TThemeInfoCacheKey, ThemeImpl] = {}
logger: structlog.stdlib.BoundLogger = structlog.get_logger() logger: structlog.stdlib.BoundLogger = structlog.get_logger()
@ -27,30 +27,18 @@ class ThemeManager(QObject):
super().__init__(parent) super().__init__(parent)
self._qPalette = QPalette() self._qPalette = QPalette()
self._customPalette: _TCustomPalette = { self._customPalette: CustomPalette = ThemeImpl.DEFAULT_CUSTOM_PALETTE
"primary": QColor.fromString("#616161"),
"success": QColor.fromString("#616161"),
"error": QColor.fromString("#616161"),
}
self._lastThemeInfo = ThemeInfo( self._lastThemeInfo = ThemeImpl().info
series="material3",
name="default",
scheme=self.getCurrentScheme(),
)
self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl()) self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl())
self._cacheMaterial3Theme(themeName="default", scheme="light") self._cacheMaterial3DynamicTheme(themeId="default")
self._cacheMaterial3Theme(themeName="default", scheme="dark") self._cacheMaterial3DynamicTheme(themeId="tempest")
self._cacheMaterial3Theme(themeName="tempest", scheme="light")
self._cacheMaterial3Theme(themeName="tempest", scheme="dark")
self._cacheMaterial3DynamicTheme(themeName="default", scheme="light") self._cacheMaterial3DynamicTheme(themeId="devilun")
self._cacheMaterial3DynamicTheme(themeName="default", scheme="dark") self._cacheMaterial3DynamicTheme(themeId="kupya")
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="light")
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="dark")
self.setTheme("material3-dynamic", "default") self.setTheme("material3-dynamic", "devilun")
def getCurrentScheme(self) -> _TScheme: def getCurrentScheme(self) -> _TScheme:
qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType] qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType]
@ -60,52 +48,47 @@ class ThemeManager(QObject):
else "light" else "light"
) )
def getMaterial3Theme(self, themeName: str, scheme: _TScheme) -> Material3ThemeImpl: def _loadQResourceJson(self, resourcePath: str):
themeDataResource = QResource(f":/themes/{themeName}.json") resource = QResource(resourcePath)
if not themeDataResource.isValid(): if not resource.isValid():
raise ValueError(f"Material3 theme {themeName!r} not found") raise ValueError(f"Resource {resourcePath!r} invalid")
themeData = json.loads( return json.loads(resource.uncompressedData().data().decode("utf-8"))
themeDataResource.uncompressedData().data().decode("utf-8")
)
def getMaterial3Theme(self, themeId: str, scheme: _TScheme) -> Material3ThemeImpl:
themeData = self._loadQResourceJson(f":/themes/m3_{themeId}.json")
return Material3ThemeImpl(themeData=themeData, scheme=scheme) return Material3ThemeImpl(themeData=themeData, scheme=scheme)
def getMaterial3DynamicTheme( def getMaterial3DynamicTheme(
self, themeName: str, scheme: _TScheme self, themeId: str, scheme: _TScheme
) -> Material3DynamicThemeImpl: ) -> Material3DynamicThemeImpl:
themeDataResource = QResource(f":/themes/{themeName}.json") themeData = self._loadQResourceJson(f":/themes/m3-dynamic_{themeId}.json")
if not themeDataResource.isValid(): return Material3DynamicThemeImpl(themeData=themeData, scheme=scheme)
raise ValueError(f"Material3 theme {themeName!r} not found")
themeData = json.loads(
themeDataResource.uncompressedData().data().decode("utf-8")
)
return Material3DynamicThemeImpl(
sourceColorHex=themeData["seed"],
scheme=scheme,
name=themeName,
)
def _cacheTheme(self, *, themeImpl: ThemeImpl): def _cacheTheme(self, *, themeImpl: ThemeImpl):
_THEME_CACHES[themeImpl.info] = themeImpl _THEME_CACHES[themeImpl.info.cacheKey()] = themeImpl
logger.debug("Theme %r cached", themeImpl.info) logger.debug("Theme %r cached", themeImpl.info)
def _getCachedTheme(self, *, themeInfo: ThemeInfo): def _getCachedTheme(self, *, key: TThemeInfoCacheKey):
cachedTheme = _THEME_CACHES.get(themeInfo) cachedTheme = _THEME_CACHES.get(key)
if cachedTheme is None: if cachedTheme is None:
raise KeyError(f"Theme {themeInfo!r} not cached") raise KeyError(f"Theme {key!r} not cached")
return cachedTheme return cachedTheme
def _cacheMaterial3Theme(self, *, themeName: str, scheme: _TScheme): def _cacheMaterial3Theme(self, *, themeId: str):
self._cacheTheme( self._cacheTheme(
themeImpl=self.getMaterial3Theme(themeName=themeName, scheme=scheme), themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="light"),
)
self._cacheTheme(
themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="dark"),
) )
def _cacheMaterial3DynamicTheme(self, *, themeName: str, scheme: _TScheme): def _cacheMaterial3DynamicTheme(self, *, themeId: str):
self._cacheTheme( self._cacheTheme(
themeImpl=self.getMaterial3DynamicTheme(themeName=themeName, scheme=scheme) themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="light")
)
self._cacheTheme(
themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="dark")
) )
@overload @overload
@ -113,29 +96,29 @@ class ThemeManager(QObject):
@overload @overload
def setTheme( def setTheme(
self, themeSeries: str, themeName: str, scheme: _TScheme | None = None, / self, themeSeries: str, themeId: str, scheme: _TScheme | None = None, /
): ... ): ...
def setTheme(self, *args, **kwargs): def setTheme(self, *args, **kwargs):
if "themeInfo" in kwargs: if "themeInfo" in kwargs:
themeInfo = kwargs["themeInfo"] cacheKey = kwargs["themeInfo"].cacheKey()
elif 2 <= len(args) <= 3: elif 2 <= len(args) <= 3:
themeSeries = args[0] themeSeries = args[0]
themeName = args[1] themeId = args[1]
schemeArg = args[2] if len(args) > 2 else None schemeArg = args[2] if len(args) > 2 else None
scheme = schemeArg or self.getCurrentScheme() scheme = schemeArg or self.getCurrentScheme()
themeInfo = ThemeInfo(series=themeSeries, name=themeName, scheme=scheme) cacheKey: TThemeInfoCacheKey = (themeSeries, themeId, scheme)
else: else:
raise TypeError("Invalid setTheme() call") raise TypeError("Invalid setTheme() call")
logger.debug("Preparing to set theme %r", themeInfo) logger.debug("Preparing to set theme %r", cacheKey)
cachedTheme = self._getCachedTheme(themeInfo=themeInfo) cachedTheme = self._getCachedTheme(key=cacheKey)
self._qPalette = cachedTheme.qPalette self._qPalette = cachedTheme.qPalette
self._customPalette = cachedTheme.customPalette self._customPalette = cachedTheme.customPalette
self._lastThemeInfo = themeInfo self._lastThemeInfo = cachedTheme.info
self._qmlExposer.themeImpl = cachedTheme self._qmlExposer.themeImpl = cachedTheme
self.themeChanged.emit() self.themeChanged.emit()
@ -152,7 +135,7 @@ class ThemeManager(QObject):
return self._qPalette return self._qPalette
@Property(dict, notify=themeChanged) @Property(dict, notify=themeChanged)
def customPalette(self) -> _TCustomPalette: def customPalette(self) -> CustomPalette:
return self._customPalette return self._customPalette
@Property(ThemeQmlExposer, notify=themeChanged) @Property(ThemeQmlExposer, notify=themeChanged)

View File

@ -1,5 +1,3 @@
from typing import TypedDict
from materialyoucolor.blend import Blend from materialyoucolor.blend import Blend
from materialyoucolor.dynamiccolor.contrast_curve import ContrastCurve from materialyoucolor.dynamiccolor.contrast_curve import ContrastCurve
from materialyoucolor.dynamiccolor.dynamic_color import DynamicColor, FromPaletteOptions from materialyoucolor.dynamiccolor.dynamic_color import DynamicColor, FromPaletteOptions
@ -9,49 +7,13 @@ from materialyoucolor.palettes.tonal_palette import TonalPalette
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme from .shared import CustomPalette, ThemeImpl, ThemeInfo, _TScheme
from .types import (
TMaterial3DynamicThemeData,
class _M3ThemeDataExtendedColorItem(TypedDict): TMaterial3ThemeData,
name: str TMaterial3ThemeDataExtendedColorItem,
color: str
description: str
harmonized: bool
_M3ThemeDataSchemes = TypedDict(
"_M3ThemeDataSchemes",
{
"light": dict[str, str],
"light-medium-contrast": dict[str, str],
"light-high-contrast": dict[str, str],
"dark": dict[str, str],
"dark-medium-contrast": dict[str, str],
"dark-high-contrast": dict[str, str],
},
) )
_M3ThemeDataPalettes = TypedDict(
"_M3ThemeDataPalettes",
{
"primary": dict[str, str],
"secondary": dict[str, str],
"tertiary": dict[str, str],
"neutral": dict[str, str],
"neutral-variant": dict[str, str],
},
)
class _M3ThemeData(TypedDict):
name: str
description: str
seed: str
coreColors: dict[str, str]
extendedColors: list[_M3ThemeDataExtendedColorItem]
schemes: _M3ThemeDataSchemes
palettes: _M3ThemeDataPalettes
def _hexToHct(hexColor: str) -> Hct: def _hexToHct(hexColor: str) -> Hct:
pureHexPart = hexColor[1:] if hexColor.startswith("#") else hexColor pureHexPart = hexColor[1:] if hexColor.startswith("#") else hexColor
@ -87,7 +49,7 @@ class Material3ThemeImpl(ThemeImpl):
QPalette.ColorRole.LinkVisited: "tertiaryContainer", QPalette.ColorRole.LinkVisited: "tertiaryContainer",
} }
def __init__(self, *, themeData: _M3ThemeData, scheme: _TScheme): def __init__(self, *, themeData: TMaterial3ThemeData, scheme: _TScheme):
self.themeData = themeData self.themeData = themeData
self.scheme: _TScheme = scheme self.scheme: _TScheme = scheme
@ -96,7 +58,7 @@ class Material3ThemeImpl(ThemeImpl):
def _findExtendedColor( def _findExtendedColor(
self, colorName: str self, colorName: str
) -> _M3ThemeDataExtendedColorItem | None: ) -> TMaterial3ThemeDataExtendedColorItem | None:
return next( return next(
(it for it in self.themeData["extendedColors"] if it["name"] == colorName), (it for it in self.themeData["extendedColors"] if it["name"] == colorName),
None, None,
@ -106,6 +68,7 @@ class Material3ThemeImpl(ThemeImpl):
def info(self): def info(self):
return ThemeInfo( return ThemeInfo(
series="material3", series="material3",
id=self.themeData["id"],
name=self.themeData["name"], name=self.themeData["name"],
scheme=self.scheme, scheme=self.scheme,
) )
@ -121,8 +84,10 @@ class Material3ThemeImpl(ThemeImpl):
return qPalette return qPalette
@property @property
def customPalette(self) -> _TCustomPalette: def customPalette(self) -> CustomPalette:
primaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["primary"]) primaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["primary"])
secondaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["secondary"])
tertiaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["tertiary"])
successColorItem = self._findExtendedColor("Success") successColorItem = self._findExtendedColor("Success")
if successColorItem is None: if successColorItem is None:
@ -133,11 +98,15 @@ class Material3ThemeImpl(ThemeImpl):
Blend.harmonize(successHct.to_int(), primaryHct.to_int()) Blend.harmonize(successHct.to_int(), primaryHct.to_int())
) )
return { return CustomPalette(
"primary": _hctToQColor(primaryHct), primary=_hctToQColor(primaryHct),
"success": _hctToQColor(successHarmonizedHct), secondary=_hctToQColor(secondaryHct),
"error": QColor.fromString(self.themeData["schemes"][self.scheme]["error"]), tertiary=_hctToQColor(tertiaryHct),
} success=_hctToQColor(successHarmonizedHct),
error=QColor.fromString(self.themeData["schemes"][self.scheme]["error"]),
toolTipBase=self.qPalette.color(QPalette.ColorRole.ToolTipBase),
toolTipText=self.qPalette.color(QPalette.ColorRole.ToolTipText),
)
class Material3DynamicThemeImpl(ThemeImpl): class Material3DynamicThemeImpl(ThemeImpl):
@ -166,23 +135,71 @@ class Material3DynamicThemeImpl(ThemeImpl):
} }
EXTENDED_COLORS = { EXTENDED_COLORS = {
"light": {
"success": "#00c555", "success": "#00c555",
"past": "#5cbad3",
"present": "#829438",
"future": "#913a79",
"beyond": "#bf0d25",
"eternal": "#8b77a4",
"pure": "#f22ec6",
"far": "#ff9028",
"lost": "#ff0c43",
},
"dark": {
"present": "#B5C76F",
"future": "#C56DAC",
"beyond": "#F24058",
"eternal": "#D3B5F9",
},
} }
def __init__(self, sourceColorHex: str, scheme: _TScheme, *, name: str): def __init__(self, themeData: TMaterial3DynamicThemeData, scheme: _TScheme):
force_scheme = themeData["options"].get("forceScheme")
if force_scheme:
is_dark = force_scheme == "dark"
else:
is_dark = scheme == "dark"
# TODO: more elegant way?
self.preferredScheme: _TScheme = scheme # for theme caching
self.actualScheme = "dark" if is_dark else "light"
self.themeId = themeData["id"]
self.themeName = themeData["name"]
self.material3Scheme = SchemeTonalSpot( self.material3Scheme = SchemeTonalSpot(
_hexToHct(sourceColorHex), _hexToHct(themeData["colors"]["primary"]),
is_dark=scheme == "dark", is_dark=is_dark,
contrast_level=0.0, contrast_level=0.0,
) )
self.name = name
secondary_color = themeData["colors"].get("secondary")
if secondary_color:
self.material3Scheme.secondary_palette = TonalPalette.from_hct(
_hexToHct(secondary_color)
)
tertiary_color = themeData["colors"].get("tertiary")
if tertiary_color:
self.material3Scheme.tertiary_palette = TonalPalette.from_hct(
_hexToHct(tertiary_color)
)
def extendedColor(self, key: str) -> str:
preferred_color = self.EXTENDED_COLORS[self.actualScheme].get(key)
if preferred_color:
return preferred_color
return self.EXTENDED_COLORS["light"][key]
@property @property
def info(self): def info(self):
return ThemeInfo( return ThemeInfo(
series="material3-dynamic", series="material3-dynamic",
name=self.name, id=self.themeId,
scheme="dark" if self.material3Scheme.is_dark else "light", name=self.themeName,
scheme=self.preferredScheme,
) )
@property @property
@ -203,27 +220,31 @@ class Material3DynamicThemeImpl(ThemeImpl):
return qPalette return qPalette
@property @property
def customPalette(self) -> _TCustomPalette: def customPalette(self) -> CustomPalette:
primaryHct = MaterialDynamicColors.primary.get_hct(self.material3Scheme) primaryHct = MaterialDynamicColors.primary.get_hct(self.material3Scheme)
secondaryHct = MaterialDynamicColors.secondary.get_hct(self.material3Scheme)
tertiaryHct = MaterialDynamicColors.tertiary.get_hct(self.material3Scheme)
errorHct = MaterialDynamicColors.error.get_hct(self.material3Scheme) errorHct = MaterialDynamicColors.error.get_hct(self.material3Scheme)
extendedPalettes: dict[str, DynamicColor] = {} extendedPalettes: dict[str, DynamicColor] = {}
for colorName, colorHex in self.EXTENDED_COLORS.items(): extendedColors = ["success"]
for colorName in extendedColors:
colorHex = self.extendedColor(colorName)
colorHct = _hexToHct(colorHex) colorHct = _hexToHct(colorHex)
colorHarmonized = Blend.harmonize(colorHct.to_int(), primaryHct.to_int()) colorHarmonized = Blend.harmonize(colorHct.to_int(), primaryHct.to_int())
colorTonalPalette = TonalPalette.from_int(colorHarmonized) colorTonalPalette = TonalPalette.from_int(colorHarmonized)
colorSurfacePaletteOptions = DynamicColor.from_palette( # colorSurfacePaletteOptions = DynamicColor.from_palette(
FromPaletteOptions( # FromPaletteOptions(
name=f"{colorName}_container", # name=f"{colorName}_container",
palette=lambda s: colorTonalPalette, # palette=lambda s: colorTonalPalette,
tone=lambda s: 30 if s.is_dark else 90, # tone=lambda s: 30 if s.is_dark else 90,
is_background=True, # is_background=True,
background=lambda s: MaterialDynamicColors.highestSurface(s), # background=lambda s: MaterialDynamicColors.highestSurface(s),
contrast_curve=ContrastCurve(1, 1, 3, 4.5), # contrast_curve=ContrastCurve(1, 1, 3, 4.5),
) # )
) # )
extendedPalettes[colorName] = DynamicColor.from_palette( extendedPalettes[colorName] = DynamicColor.from_palette(
FromPaletteOptions( FromPaletteOptions(
@ -236,10 +257,40 @@ class Material3DynamicThemeImpl(ThemeImpl):
) )
) )
return { # arcaea colors
"primary": _hctToQColor(primaryHct), arcaeaColors: dict[str, Hct] = {}
"success": _hctToQColor( arcaeaColorKeys = [
"past",
"present",
"future",
"beyond",
"eternal",
"pure",
"far",
"lost",
]
for colorName in arcaeaColorKeys:
colorHex = self.extendedColor(colorName)
colorHct = _hexToHct(colorHex)
colorHarmonized = Blend.harmonize(colorHct.to_int(), primaryHct.to_int())
arcaeaColors[colorName] = Hct.from_int(colorHarmonized)
return CustomPalette(
primary=_hctToQColor(primaryHct),
secondary=_hctToQColor(secondaryHct),
tertiary=_hctToQColor(tertiaryHct),
success=_hctToQColor(
extendedPalettes["success"].get_hct(self.material3Scheme) extendedPalettes["success"].get_hct(self.material3Scheme)
), ),
"error": _hctToQColor(errorHct), error=_hctToQColor(errorHct),
} toolTipBase=self.qPalette.color(QPalette.ColorRole.ToolTipBase),
toolTipText=self.qPalette.color(QPalette.ColorRole.ToolTipText),
past=_hctToQColor(arcaeaColors["past"]),
present=_hctToQColor(arcaeaColors["present"]),
future=_hctToQColor(arcaeaColors["future"]),
beyond=_hctToQColor(arcaeaColors["beyond"]),
eternal=_hctToQColor(arcaeaColors["eternal"]),
pure=_hctToQColor(arcaeaColors["pure"]),
far=_hctToQColor(arcaeaColors["far"]),
lost=_hctToQColor(arcaeaColors["lost"]),
)

View File

@ -26,12 +26,60 @@ class ThemeQmlExposer(QObject):
@Property(QColor, notify=themeChanged) @Property(QColor, notify=themeChanged)
def primary(self): def primary(self):
return self._themeImpl.customPalette["primary"] return self._themeImpl.customPalette.primary
@Property(QColor, notify=themeChanged)
def secondary(self):
return self._themeImpl.customPalette.secondary
@Property(QColor, notify=themeChanged)
def tertiary(self):
return self._themeImpl.customPalette.tertiary
@Property(QColor, notify=themeChanged) @Property(QColor, notify=themeChanged)
def success(self): def success(self):
return self._themeImpl.customPalette["success"] return self._themeImpl.customPalette.success
@Property(QColor, notify=themeChanged) @Property(QColor, notify=themeChanged)
def error(self): def error(self):
return self._themeImpl.customPalette["error"] return self._themeImpl.customPalette.error
@Property(QColor, notify=themeChanged)
def toolTipBase(self):
return self._themeImpl.customPalette.toolTipBase
@Property(QColor, notify=themeChanged)
def toolTipText(self):
return self._themeImpl.customPalette.toolTipText
@Property(QColor, notify=themeChanged)
def past(self):
return self._themeImpl.customPalette.past
@Property(QColor, notify=themeChanged)
def present(self):
return self._themeImpl.customPalette.present
@Property(QColor, notify=themeChanged)
def future(self):
return self._themeImpl.customPalette.future
@Property(QColor, notify=themeChanged)
def beyond(self):
return self._themeImpl.customPalette.beyond
@Property(QColor, notify=themeChanged)
def eternal(self):
return self._themeImpl.customPalette.eternal
@Property(QColor, notify=themeChanged)
def pure(self):
return self._themeImpl.customPalette.pure
@Property(QColor, notify=themeChanged)
def far(self):
return self._themeImpl.customPalette.far
@Property(QColor, notify=themeChanged)
def lost(self):
return self._themeImpl.customPalette.lost

View File

@ -1,43 +1,62 @@
from dataclasses import dataclass from dataclasses import dataclass, field
from typing import Literal, TypedDict from typing import Literal
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
class _TCustomPalette(TypedDict): @dataclass(kw_only=True)
primary: QColor class CustomPalette:
success: QColor primary: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
error: QColor secondary: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
tertiary: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
success: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
error: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
toolTipBase: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
toolTipText: QColor = field(default_factory=lambda: QColor.fromRgb(0x616161))
past: QColor = field(default_factory=lambda: QColor.fromRgb(0x5CBAD3))
present: QColor = field(default_factory=lambda: QColor.fromRgb(0x829438))
future: QColor = field(default_factory=lambda: QColor.fromRgb(0x913A79))
beyond: QColor = field(default_factory=lambda: QColor.fromRgb(0xBF0D25))
eternal: QColor = field(default_factory=lambda: QColor.fromRgb(0x8B77A4))
pure: QColor = field(default_factory=lambda: QColor.fromRgb(0xF22EC6))
far: QColor = field(default_factory=lambda: QColor.fromRgb(0xFF9028))
lost: QColor = field(default_factory=lambda: QColor.fromRgb(0xFF0C43))
_TScheme = Literal["light", "dark"] _TScheme = Literal["light", "dark"]
TThemeInfoCacheKey = tuple[str, str, _TScheme]
@dataclass @dataclass
class ThemeInfo: class ThemeInfo:
series: str series: str
id: str
name: str name: str
scheme: _TScheme scheme: _TScheme
def __hash__(self) -> int: def cacheKey(self) -> TThemeInfoCacheKey:
return hash((self.series, self.name, self.scheme)) return (self.series, self.id, self.scheme)
class ThemeImpl: class ThemeImpl:
DEFAULT_CUSTOM_PALETTE = { DEFAULT_CUSTOM_PALETTE: CustomPalette = CustomPalette()
"primary": QColor.fromString("#616161"),
"success": QColor.fromString("#616161"),
"error": QColor.fromString("#616161"),
}
@property @property
def info(self) -> ThemeInfo: def info(self) -> ThemeInfo:
return ThemeInfo(series="placeholder", name="placeholder", scheme="dark") return ThemeInfo(
series="placeholder",
id="placeholder",
name="placeholder",
scheme="dark",
)
@property @property
def qPalette(self) -> QPalette: def qPalette(self) -> QPalette:
return QPalette() return QPalette()
@property @property
def customPalette(self) -> _TCustomPalette: def customPalette(self) -> CustomPalette:
return self.DEFAULT_CUSTOM_PALETTE # pyright: ignore[reportReturnType] return self.DEFAULT_CUSTOM_PALETTE # pyright: ignore[reportReturnType]

72
ui/theme/types.py Normal file
View File

@ -0,0 +1,72 @@
from typing import TypedDict
from .shared import _TScheme
# region material3
class TMaterial3ThemeDataExtendedColorItem(TypedDict):
name: str
color: str
description: str
harmonized: bool
TMaterial3ThemeDataSchemes = TypedDict(
"TMaterial3ThemeDataSchemes",
{
"light": dict[str, str],
"light-medium-contrast": dict[str, str],
"light-high-contrast": dict[str, str],
"dark": dict[str, str],
"dark-medium-contrast": dict[str, str],
"dark-high-contrast": dict[str, str],
},
)
TMaterial3ThemeDataPalettes = TypedDict(
"TMaterial3ThemeDataPalettes",
{
"primary": dict[str, str],
"secondary": dict[str, str],
"tertiary": dict[str, str],
"neutral": dict[str, str],
"neutral-variant": dict[str, str],
},
)
class TMaterial3ThemeData(TypedDict):
id: str
name: str
description: str
seed: str
coreColors: dict[str, str]
extendedColors: list[TMaterial3ThemeDataExtendedColorItem]
schemes: TMaterial3ThemeDataSchemes
palettes: TMaterial3ThemeDataPalettes
# endregion
# region material3-dynamic
class TMaterial3DynamicThemeDataColors(TypedDict):
primary: str
secondary: str | None
tertiary: str | None
class TMaterial3DynamicThemeDataOptions(TypedDict):
forceScheme: _TScheme | None
class TMaterial3DynamicThemeData(TypedDict):
id: str
name: str
colors: TMaterial3DynamicThemeDataColors
options: TMaterial3DynamicThemeDataOptions
# endregion