Icons for genres with backgrounds, text, and ability to specify text color and font family (#34)
@@ -141,8 +141,10 @@ BONOB_NAVIDROME_URL | http://$(hostname):4533 | URL for navidrome
|
|||||||
BONOB_NAVIDROME_CUSTOM_CLIENTS | undefined | Comma delimeted mime types for custom navidrome clients when streaming. ie. "audio/flac,audio/ogg" would use client = 'bonob+audio/flac' for flacs, and 'bonob+audio/ogg' for oggs.
|
BONOB_NAVIDROME_CUSTOM_CLIENTS | undefined | Comma delimeted mime types for custom navidrome clients when streaming. ie. "audio/flac,audio/ogg" would use client = 'bonob+audio/flac' for flacs, and 'bonob+audio/ogg' for oggs.
|
||||||
BONOB_SCROBBLE_TRACKS | true | Whether to scrobble the playing of a track if it has been played for >30s
|
BONOB_SCROBBLE_TRACKS | true | Whether to scrobble the playing of a track if it has been played for >30s
|
||||||
BONOB_REPORT_NOW_PLAYING | true | Whether to report a track as now playing
|
BONOB_REPORT_NOW_PLAYING | true | Whether to report a track as now playing
|
||||||
BONOB_ICON_FOREGROUND_COLOR | undefined | Icon foreground color in sonos app, must be a valid [web color](https://www.december.com/html/spec/colorsvg.html)
|
BONOB_ICON_FOREGROUND_COLOR | undefined | Icon foreground color in sonos app, must be a valid [svg color](https://www.december.com/html/spec/colorsvg.html)
|
||||||
BONOB_ICON_BACKGROUND_COLOR | undefined | Icon background color in sonos app, must be a valid [web color](https://www.december.com/html/spec/colorsvg.html)
|
BONOB_ICON_BACKGROUND_COLOR | undefined | Icon background color in sonos app, must be a valid [svg color](https://www.december.com/html/spec/colorsvg.html)
|
||||||
|
BONOB_ICON_FONT_COLOR | undefined | Icon font color in sonos app, must be a valid [svg color](https://www.december.com/html/spec/colorsvg.html)
|
||||||
|
BONOB_ICON_FONT_FAMILY | undefined | Icon font family in sonos app
|
||||||
|
|
||||||
## Initialising service within sonos app
|
## Initialising service within sonos app
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ BONOB_ICON_BACKGROUND_COLOR | undefined | Icon background color in sonos app, mu
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- Icons courtesy of: [Navidrome](https://www.navidrome.org/), [Vectornator](https://www.vectornator.io/), and @jicho
|
- Icons courtesy of: [Navidrome](https://www.navidrome.org/), [Vectornator](https://www.vectornator.io/icons), and @jicho
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"watch": ["src/**/*.ts", "web/**/*.svg"],
|
|
||||||
"ignore": [],
|
|
||||||
"exec": "ts-node ./src/app.ts"
|
|
||||||
}
|
|
||||||
@@ -50,8 +50,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -Rf build",
|
"clean": "rm -Rf build",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true nodemon",
|
"dev": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true nodemon -V ./src/app.ts",
|
||||||
"devr": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true BONOB_SONOS_AUTO_REGISTER=true nodemon",
|
"devr": "BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true BONOB_SONOS_AUTO_REGISTER=true nodemon -V ./src/app.ts",
|
||||||
"register-dev": "ts-node ./src/register.ts http://$(hostname):4534",
|
"register-dev": "ts-node ./src/register.ts http://$(hostname):4534",
|
||||||
"test": "jest --testPathIgnorePatterns=build"
|
"test": "jest --testPathIgnorePatterns=build"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function () {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const colorFrom = (envVar: string) => {
|
const wordFrom = (envVar: string) => {
|
||||||
const value = process.env[envVar];
|
const value = process.env[envVar];
|
||||||
if (value && value != "") {
|
if (value && value != "") {
|
||||||
if (value.match(/^\w+$/)) return value;
|
if (value.match(/^\w+$/)) return value;
|
||||||
@@ -31,8 +31,10 @@ export default function () {
|
|||||||
bonobUrl: url(bonobUrl),
|
bonobUrl: url(bonobUrl),
|
||||||
secret: process.env["BONOB_SECRET"] || "bonob",
|
secret: process.env["BONOB_SECRET"] || "bonob",
|
||||||
icons: {
|
icons: {
|
||||||
foregroundColor: colorFrom("BONOB_ICON_FOREGROUND_COLOR"),
|
foregroundColor: wordFrom("BONOB_ICON_FOREGROUND_COLOR"),
|
||||||
backgroundColor: colorFrom("BONOB_ICON_BACKGROUND_COLOR"),
|
backgroundColor: wordFrom("BONOB_ICON_BACKGROUND_COLOR"),
|
||||||
|
fontColor: wordFrom("BONOB_ICON_FONT_COLOR"),
|
||||||
|
fontFamily: wordFrom("BONOB_ICON_FONT_FAMILY")
|
||||||
},
|
},
|
||||||
sonos: {
|
sonos: {
|
||||||
serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob",
|
serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob",
|
||||||
|
|||||||
242
src/icon.ts
@@ -1,11 +1,24 @@
|
|||||||
import libxmljs, { Element, Attribute } from "libxmljs2";
|
import libxmljs, { Element, Attribute } from "libxmljs2";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
import { Clock, isChristmas, isCNY, isHalloween, isHoli, SystemClock } from "./clock";
|
import fs from "fs";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Clock,
|
||||||
|
isChristmas,
|
||||||
|
isCNY,
|
||||||
|
isHalloween,
|
||||||
|
isHoli,
|
||||||
|
SystemClock,
|
||||||
|
} from "./clock";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export type Transformation = {
|
export type Transformation = {
|
||||||
viewPortIncreasePercent: number | undefined;
|
viewPortIncreasePercent: number | undefined;
|
||||||
backgroundColor: string | undefined;
|
backgroundColor: string | undefined;
|
||||||
foregroundColor: string | undefined;
|
foregroundColor: string | undefined;
|
||||||
|
text: string | undefined;
|
||||||
|
fontColor: string | undefined;
|
||||||
|
fontFamily: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SVG_NS = {
|
const SVG_NS = {
|
||||||
@@ -43,13 +56,23 @@ export interface Icon {
|
|||||||
|
|
||||||
export class ColorOverridingIcon implements Icon {
|
export class ColorOverridingIcon implements Icon {
|
||||||
rule: () => Boolean;
|
rule: () => Boolean;
|
||||||
newColors: () => Pick<Transformation, "backgroundColor" | "foregroundColor">;
|
newColors: () => Partial<
|
||||||
|
Pick<
|
||||||
|
Transformation,
|
||||||
|
"backgroundColor" | "foregroundColor" | "fontColor" | "fontFamily"
|
||||||
|
>
|
||||||
|
>;
|
||||||
icon: Icon;
|
icon: Icon;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
rule: () => Boolean,
|
rule: () => Boolean,
|
||||||
newColors: () => Pick<Transformation, "backgroundColor" | "foregroundColor">
|
newColors: () => Partial<
|
||||||
|
Pick<
|
||||||
|
Transformation,
|
||||||
|
"backgroundColor" | "foregroundColor" | "fontColor" | "fontFamily"
|
||||||
|
>
|
||||||
|
>
|
||||||
) {
|
) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.rule = rule;
|
this.rule = rule;
|
||||||
@@ -74,6 +97,9 @@ export class SvgIcon implements Icon {
|
|||||||
viewPortIncreasePercent: undefined,
|
viewPortIncreasePercent: undefined,
|
||||||
backgroundColor: undefined,
|
backgroundColor: undefined,
|
||||||
foregroundColor: undefined,
|
foregroundColor: undefined,
|
||||||
|
text: undefined,
|
||||||
|
fontColor: undefined,
|
||||||
|
fontFamily: undefined,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
this.svg = svg;
|
this.svg = svg;
|
||||||
@@ -106,26 +132,62 @@ export class SvgIcon implements Icon {
|
|||||||
y: `${viewBox.minY}`,
|
y: `${viewBox.minY}`,
|
||||||
width: `${Math.abs(viewBox.minX) + viewBox.width}`,
|
width: `${Math.abs(viewBox.minX) + viewBox.width}`,
|
||||||
height: `${Math.abs(viewBox.minY) + viewBox.height}`,
|
height: `${Math.abs(viewBox.minY) + viewBox.height}`,
|
||||||
style: `fill:${this.transformation.backgroundColor}`,
|
fill: this.transformation.backgroundColor,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (this.transformation.foregroundColor) {
|
if (this.transformation.foregroundColor) {
|
||||||
(xml.find("//svg:path", SVG_NS) as Element[]).forEach((path) =>
|
(xml.find("//svg:path", SVG_NS) as Element[]).forEach((path) => {
|
||||||
path.attr({ style: `fill:${this.transformation.foregroundColor}` })
|
if (path.attr("fill"))
|
||||||
|
path.attr({ stroke: this.transformation.foregroundColor! });
|
||||||
|
else path.attr({ fill: this.transformation.foregroundColor! });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.transformation.text) {
|
||||||
|
const w = Math.abs(viewBox.minX) + Math.abs(viewBox.width);
|
||||||
|
const h = Math.abs(viewBox.minY) + Math.abs(viewBox.height);
|
||||||
|
const i = Math.floor(0.1 * w);
|
||||||
|
let attr: any = {
|
||||||
|
"font-size": `${Math.floor(h / 4)}`,
|
||||||
|
"font-weight": "bold",
|
||||||
|
};
|
||||||
|
if (this.transformation.fontFamily)
|
||||||
|
attr = { ...attr, "font-family": this.transformation.fontFamily };
|
||||||
|
if (this.transformation.fontColor)
|
||||||
|
attr = { ...attr, style: `fill:${this.transformation.fontColor}` };
|
||||||
|
const g = new Element(xml, "g");
|
||||||
|
g.attr(attr);
|
||||||
|
(xml.get("//svg:svg", SVG_NS) as Element).addChild(
|
||||||
|
g.addChild(
|
||||||
|
new Element(xml, "text")
|
||||||
|
.attr({
|
||||||
|
x: `${viewBox.minX + i}`,
|
||||||
|
y: `${viewBox.minY + Math.floor(0.8 * h)}`,
|
||||||
|
})
|
||||||
|
.text(this.transformation.text)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return xml.toString();
|
return xml.toString();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HOLI_COLORS = ["#06bceb", "#9fc717", "#fbdc10", "#f00b9a", "#fa9705"]
|
export const HOLI_COLORS = [
|
||||||
|
"#06bceb",
|
||||||
|
"#9fc717",
|
||||||
|
"#fbdc10",
|
||||||
|
"#f00b9a",
|
||||||
|
"#fa9705",
|
||||||
|
];
|
||||||
|
|
||||||
export const makeFestive = (icon: Icon, clock: Clock = SystemClock): Icon => {
|
export const makeFestive = (icon: Icon, clock: Clock = SystemClock): Icon => {
|
||||||
const wrap = (
|
const wrap = (
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
rule: (clock: Clock) => boolean,
|
rule: (clock: Clock) => boolean,
|
||||||
colors: Pick<Transformation, "backgroundColor" | "foregroundColor">
|
colors: Pick<
|
||||||
|
Transformation,
|
||||||
|
"backgroundColor" | "foregroundColor" | "fontColor"
|
||||||
|
>
|
||||||
) =>
|
) =>
|
||||||
new ColorOverridingIcon(
|
new ColorOverridingIcon(
|
||||||
icon,
|
icon,
|
||||||
@@ -133,22 +195,176 @@ export const makeFestive = (icon: Icon, clock: Clock = SystemClock): Icon => {
|
|||||||
() => colors
|
() => colors
|
||||||
);
|
);
|
||||||
|
|
||||||
const xmas = wrap(icon, isChristmas, {
|
let result = icon;
|
||||||
|
|
||||||
|
const apply = (
|
||||||
|
rule: (clock: Clock) => boolean,
|
||||||
|
colors: Pick<
|
||||||
|
Transformation,
|
||||||
|
"backgroundColor" | "foregroundColor" | "fontColor"
|
||||||
|
>
|
||||||
|
) => (result = wrap(result, rule, colors));
|
||||||
|
|
||||||
|
apply(isChristmas, {
|
||||||
backgroundColor: "green",
|
backgroundColor: "green",
|
||||||
foregroundColor: "red",
|
foregroundColor: "red",
|
||||||
|
fontColor: "white",
|
||||||
});
|
});
|
||||||
|
|
||||||
const randomHoliColors = _.shuffle([...HOLI_COLORS]);
|
const randomHoliColors = _.shuffle([...HOLI_COLORS]);
|
||||||
const holi = wrap(xmas, isHoli, {
|
apply(isHoli, {
|
||||||
backgroundColor: randomHoliColors.pop(),
|
backgroundColor: randomHoliColors.pop(),
|
||||||
foregroundColor: randomHoliColors.pop(),
|
foregroundColor: randomHoliColors.pop(),
|
||||||
|
fontColor: randomHoliColors.pop(),
|
||||||
});
|
});
|
||||||
const cny = wrap(holi, isCNY, {
|
|
||||||
|
apply(isCNY, {
|
||||||
backgroundColor: "red",
|
backgroundColor: "red",
|
||||||
foregroundColor: "yellow",
|
foregroundColor: "yellow",
|
||||||
|
fontColor: "crimson",
|
||||||
});
|
});
|
||||||
const halloween = wrap(cny, isHalloween, {
|
|
||||||
|
apply(isHalloween, {
|
||||||
backgroundColor: "orange",
|
backgroundColor: "orange",
|
||||||
foregroundColor: "black",
|
foregroundColor: "black",
|
||||||
|
fontColor: "orangered",
|
||||||
});
|
});
|
||||||
return halloween;
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ICON =
|
||||||
|
| "artists"
|
||||||
|
| "albums"
|
||||||
|
| "playlists"
|
||||||
|
| "genres"
|
||||||
|
| "random"
|
||||||
|
| "starred"
|
||||||
|
| "recentlyAdded"
|
||||||
|
| "recentlyPlayed"
|
||||||
|
| "mostPlayed"
|
||||||
|
| "discover"
|
||||||
|
| "blank"
|
||||||
|
| "mushroom"
|
||||||
|
| "african"
|
||||||
|
| "rock"
|
||||||
|
| "metal"
|
||||||
|
| "punk"
|
||||||
|
| "americana"
|
||||||
|
| "guitar"
|
||||||
|
| "book"
|
||||||
|
| "oz"
|
||||||
|
| "rap"
|
||||||
|
| "horror"
|
||||||
|
| "hipHop"
|
||||||
|
| "pop"
|
||||||
|
| "blues"
|
||||||
|
| "classical"
|
||||||
|
| "comedy"
|
||||||
|
| "vinyl"
|
||||||
|
| "electronic"
|
||||||
|
| "pills"
|
||||||
|
| "trumpet"
|
||||||
|
| "conductor"
|
||||||
|
| "reggae"
|
||||||
|
| "music";
|
||||||
|
|
||||||
|
const iconFrom = (name: string) =>
|
||||||
|
new SvgIcon(
|
||||||
|
fs
|
||||||
|
.readFileSync(path.resolve(__dirname, "..", "web", "icons", name))
|
||||||
|
.toString()
|
||||||
|
).with({ viewPortIncreasePercent: 50 });
|
||||||
|
|
||||||
|
export const ICONS: Record<ICON, Icon> = {
|
||||||
|
artists: iconFrom("navidrome-artists.svg"),
|
||||||
|
albums: iconFrom("navidrome-all.svg"),
|
||||||
|
blank: iconFrom("blank.svg"),
|
||||||
|
playlists: iconFrom("navidrome-playlists.svg"),
|
||||||
|
genres: iconFrom("Theatre-Mask-111172.svg"),
|
||||||
|
random: iconFrom("navidrome-random.svg"),
|
||||||
|
starred: iconFrom("navidrome-topRated.svg"),
|
||||||
|
recentlyAdded: iconFrom("navidrome-recentlyAdded.svg"),
|
||||||
|
recentlyPlayed: iconFrom("navidrome-recentlyPlayed.svg"),
|
||||||
|
mostPlayed: iconFrom("navidrome-mostPlayed.svg"),
|
||||||
|
discover: iconFrom("Binoculars-14310.svg"),
|
||||||
|
mushroom: iconFrom("Mushroom-63864.svg"),
|
||||||
|
african: iconFrom("Africa-48087.svg"),
|
||||||
|
rock: iconFrom("Rock-Music-11007.svg"),
|
||||||
|
metal: iconFrom("Metal-Music-17763.svg"),
|
||||||
|
punk: iconFrom("Punk-40450.svg"),
|
||||||
|
americana: iconFrom("US-Capitol-104805.svg"),
|
||||||
|
guitar: iconFrom("Guitar-110433.svg"),
|
||||||
|
book: iconFrom("Book-453.svg"),
|
||||||
|
oz: iconFrom("Kangaroo-16730.svg"),
|
||||||
|
hipHop: iconFrom("Hip-Hop Music-17757.svg"),
|
||||||
|
rap: iconFrom("Rap-24851.svg"),
|
||||||
|
horror: iconFrom("Horror-4387.svg"),
|
||||||
|
pop: iconFrom("Ice-Pop Yellow-94532.svg"),
|
||||||
|
blues: iconFrom("Blues-113548.svg"),
|
||||||
|
classical: iconFrom("Classic-Music-11646.svg"),
|
||||||
|
comedy: iconFrom("Comedy-2-599.svg"),
|
||||||
|
vinyl: iconFrom("Music-Record-102104.svg"),
|
||||||
|
electronic: iconFrom("Electronic-Music-17745.svg"),
|
||||||
|
pills: iconFrom("Pills-112386.svg"),
|
||||||
|
trumpet: iconFrom("Trumpet-17823.svg"),
|
||||||
|
conductor: iconFrom("Music-Conductor-225.svg"),
|
||||||
|
reggae: iconFrom("Reggae-24843.svg"),
|
||||||
|
music: iconFrom("Music-14097.svg")
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RULE = (genre: string) => boolean;
|
||||||
|
|
||||||
|
const eq =
|
||||||
|
(expected: string): RULE =>
|
||||||
|
(value: string) =>
|
||||||
|
expected.toLowerCase() === value.toLowerCase();
|
||||||
|
|
||||||
|
const contains =
|
||||||
|
(expected: string): RULE =>
|
||||||
|
(value: string) =>
|
||||||
|
value.toLowerCase().includes(expected.toLowerCase());
|
||||||
|
|
||||||
|
const containsWithAllTheNonWordCharsRemoved =
|
||||||
|
(expected: string): RULE =>
|
||||||
|
(value: string) =>
|
||||||
|
value.replace(/\W+/, " ").toLowerCase().includes(expected.toLowerCase());
|
||||||
|
|
||||||
|
const GENRE_RULES: [RULE, ICON][] = [
|
||||||
|
[eq("Acid House"), "mushroom"],
|
||||||
|
[contains("Goa"), "mushroom"],
|
||||||
|
[contains("Psy"), "mushroom"],
|
||||||
|
[eq("African"), "african"],
|
||||||
|
[eq("Americana"), "americana"],
|
||||||
|
[contains("Rock"), "rock"],
|
||||||
|
[contains("Folk"), "guitar"],
|
||||||
|
[contains("Book"), "book"],
|
||||||
|
[contains("Australian"), "oz"],
|
||||||
|
[contains("Rap"), "rap"],
|
||||||
|
[containsWithAllTheNonWordCharsRemoved("Hip Hop"), "hipHop"],
|
||||||
|
[contains("Horror"), "horror"],
|
||||||
|
[contains("Metal"), "metal"],
|
||||||
|
[contains("Punk"), "punk"],
|
||||||
|
[contains("Pop"), "pop"],
|
||||||
|
[contains("Blues"), "blues"],
|
||||||
|
[contains("Classical"), "classical"],
|
||||||
|
[contains("Comedy"), "comedy"],
|
||||||
|
[contains("Dub"), "vinyl"],
|
||||||
|
[contains("Turntable"), "vinyl"],
|
||||||
|
[contains("Electro"), "electronic"],
|
||||||
|
[contains("Trance"), "pills"],
|
||||||
|
[contains("Techno"), "pills"],
|
||||||
|
[contains("House"), "pills"],
|
||||||
|
[contains("Rave"), "pills"],
|
||||||
|
[contains("Jazz"), "trumpet"],
|
||||||
|
[contains("Orchestra"), "conductor"],
|
||||||
|
[contains("Reggae"), "reggae"],
|
||||||
|
];
|
||||||
|
|
||||||
|
export function iconForGenre(genre: string): ICON {
|
||||||
|
const [_, name] = GENRE_RULES.find(([rule, _]) => rule(genre)) || [
|
||||||
|
"music",
|
||||||
|
"music",
|
||||||
|
];
|
||||||
|
return name! as ICON;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import * as Eta from "eta";
|
|||||||
import morgan from "morgan";
|
import morgan from "morgan";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import fs from "fs";
|
|
||||||
|
|
||||||
import { PassThrough, Transform, TransformCallback } from "stream";
|
import { PassThrough, Transform, TransformCallback } from "stream";
|
||||||
|
|
||||||
@@ -17,7 +16,6 @@ import {
|
|||||||
LOGIN_ROUTE,
|
LOGIN_ROUTE,
|
||||||
CREATE_REGISTRATION_ROUTE,
|
CREATE_REGISTRATION_ROUTE,
|
||||||
REMOVE_REGISTRATION_ROUTE,
|
REMOVE_REGISTRATION_ROUTE,
|
||||||
ICON,
|
|
||||||
} from "./smapi";
|
} from "./smapi";
|
||||||
import { LinkCodes, InMemoryLinkCodes } from "./link_codes";
|
import { LinkCodes, InMemoryLinkCodes } from "./link_codes";
|
||||||
import { MusicService, isSuccess } from "./music_service";
|
import { MusicService, isSuccess } from "./music_service";
|
||||||
@@ -28,7 +26,7 @@ import { Clock, SystemClock } from "./clock";
|
|||||||
import { pipe } from "fp-ts/lib/function";
|
import { pipe } from "fp-ts/lib/function";
|
||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
import makeI8N, { asLANGs, KEY, keys as i8nKeys, LANG } from "./i8n";
|
import makeI8N, { asLANGs, KEY, keys as i8nKeys, LANG } from "./i8n";
|
||||||
import { SvgIcon, Icon, makeFestive } from "./icon";
|
import { Icon, makeFestive, ICONS } from "./icon";
|
||||||
|
|
||||||
export const BONOB_ACCESS_TOKEN_HEADER = "bonob-access-token";
|
export const BONOB_ACCESS_TOKEN_HEADER = "bonob-access-token";
|
||||||
|
|
||||||
@@ -103,27 +101,6 @@ function server(
|
|||||||
return i8n(...asLANGs(req.headers["accept-language"]));
|
return i8n(...asLANGs(req.headers["accept-language"]));
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconFrom = (name: string) =>
|
|
||||||
makeFestive(
|
|
||||||
new SvgIcon(
|
|
||||||
fs.readFileSync(path.resolve(__dirname, "..", "web", "icons", name)).toString()
|
|
||||||
).with({ viewPortIncreasePercent: 50, ...iconColors }),
|
|
||||||
clock
|
|
||||||
);
|
|
||||||
|
|
||||||
const ICONS: Record<ICON, Icon> = {
|
|
||||||
artists: iconFrom("navidrome-artists.svg"),
|
|
||||||
albums: iconFrom("navidrome-all.svg"),
|
|
||||||
playlists: iconFrom("navidrome-playlists.svg"),
|
|
||||||
genres: iconFrom("Theatre-Mask-111172.svg"),
|
|
||||||
random: iconFrom("navidrome-random.svg"),
|
|
||||||
starred: iconFrom("navidrome-topRated.svg"),
|
|
||||||
recentlyAdded: iconFrom("navidrome-recentlyAdded.svg"),
|
|
||||||
recentlyPlayed: iconFrom("navidrome-recentlyPlayed.svg"),
|
|
||||||
mostPlayed: iconFrom("navidrome-mostPlayed.svg"),
|
|
||||||
discover: iconFrom("Binoculars-14310.svg"),
|
|
||||||
};
|
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
const lang = langFor(req);
|
const lang = langFor(req);
|
||||||
Promise.all([sonos.devices(), sonos.services()]).then(
|
Promise.all([sonos.devices(), sonos.services()]).then(
|
||||||
@@ -383,6 +360,9 @@ function server(
|
|||||||
app.get("/icon/:type/size/:size", (req, res) => {
|
app.get("/icon/:type/size/:size", (req, res) => {
|
||||||
const type = req.params["type"]!;
|
const type = req.params["type"]!;
|
||||||
const size = req.params["size"]!;
|
const size = req.params["size"]!;
|
||||||
|
const text: string | undefined = req.query.text
|
||||||
|
? (req.query.text as string)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
if (!Object.keys(ICONS).includes(type)) {
|
if (!Object.keys(ICONS).includes(type)) {
|
||||||
return res.status(404).send();
|
return res.status(404).send();
|
||||||
@@ -392,7 +372,7 @@ function server(
|
|||||||
) {
|
) {
|
||||||
return res.status(400).send();
|
return res.status(400).send();
|
||||||
} else {
|
} else {
|
||||||
const icon = (ICONS as any)[type]! as Icon;
|
let icon = (ICONS as any)[type]! as Icon;
|
||||||
const spec =
|
const spec =
|
||||||
size == "legacy"
|
size == "legacy"
|
||||||
? {
|
? {
|
||||||
@@ -406,7 +386,9 @@ function server(
|
|||||||
Promise.resolve(svg),
|
Promise.resolve(svg),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Promise.resolve(icon.toString())
|
return Promise.resolve(
|
||||||
|
makeFestive(icon.with({ text, ...iconColors }), clock).toString()
|
||||||
|
)
|
||||||
.then(spec.responseFormatter)
|
.then(spec.responseFormatter)
|
||||||
.then((data) => res.status(200).type(spec.mimeType).send(data));
|
.then((data) => res.status(200).type(spec.mimeType).send(data));
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/smapi.ts
@@ -23,6 +23,7 @@ import { BONOB_ACCESS_TOKEN_HEADER } from "./server";
|
|||||||
import { Clock } from "./clock";
|
import { Clock } from "./clock";
|
||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
import { asLANGs, I8N } from "./i8n";
|
import { asLANGs, I8N } from "./i8n";
|
||||||
|
import { ICON, iconForGenre } from "./icon";
|
||||||
|
|
||||||
export const LOGIN_ROUTE = "/login";
|
export const LOGIN_ROUTE = "/login";
|
||||||
export const CREATE_REGISTRATION_ROUTE = "/registration/add";
|
export const CREATE_REGISTRATION_ROUTE = "/registration/add";
|
||||||
@@ -46,8 +47,6 @@ export const SONOS_RECOMMENDED_IMAGE_SIZES = [
|
|||||||
"1500",
|
"1500",
|
||||||
];
|
];
|
||||||
|
|
||||||
export type ICON = "artists" | "albums" | "playlists" | "genres" | "random" | "starred" | "recentlyAdded" | "recentlyPlayed" | "mostPlayed" | "discover"
|
|
||||||
|
|
||||||
const WSDL_FILE = path.resolve(
|
const WSDL_FILE = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
"Sonoswsdl-1.19.4-20190411.142401-3.wsdl"
|
"Sonoswsdl-1.19.4-20190411.142401-3.wsdl"
|
||||||
@@ -209,10 +208,11 @@ export type Container = {
|
|||||||
displayType: string | undefined;
|
displayType: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const genre = (genre: Genre) => ({
|
const genre = (bonobUrl: URLBuilder, genre: Genre) => ({
|
||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre.id}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre.name,
|
title: genre.name,
|
||||||
|
albumArtURI: iconArtURI(bonobUrl, iconForGenre(genre.name), genre.name).href(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const playlist = (playlist: PlaylistSummary) => ({
|
const playlist = (playlist: PlaylistSummary) => ({
|
||||||
@@ -230,8 +230,8 @@ const playlist = (playlist: PlaylistSummary) => ({
|
|||||||
export const defaultAlbumArtURI = (bonobUrl: URLBuilder, album: AlbumSummary) =>
|
export const defaultAlbumArtURI = (bonobUrl: URLBuilder, album: AlbumSummary) =>
|
||||||
bonobUrl.append({ pathname: `/art/album/${album.id}/size/180` });
|
bonobUrl.append({ pathname: `/art/album/${album.id}/size/180` });
|
||||||
|
|
||||||
export const iconArtURI = (bonobUrl: URLBuilder, icon: ICON) =>
|
export const iconArtURI = (bonobUrl: URLBuilder, icon: ICON, text: string | undefined = undefined) =>
|
||||||
bonobUrl.append({ pathname: `/icon/${icon}/size/legacy` });
|
bonobUrl.append({ pathname: `/icon/${icon}/size/legacy`, searchParams: text ? { text } : {} });
|
||||||
|
|
||||||
export const defaultArtistArtURI = (
|
export const defaultArtistArtURI = (
|
||||||
bonobUrl: URLBuilder,
|
bonobUrl: URLBuilder,
|
||||||
@@ -688,7 +688,7 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
.then(slice2(paging))
|
.then(slice2(paging))
|
||||||
.then(([page, total]) =>
|
.then(([page, total]) =>
|
||||||
getMetadataResult({
|
getMetadataResult({
|
||||||
mediaCollection: page.map(genre),
|
mediaCollection: page.map(it => genre(bonobUrl, it)),
|
||||||
index: paging._index,
|
index: paging._index,
|
||||||
total,
|
total,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -132,7 +132,9 @@ describe("config", () => {
|
|||||||
describe("when BONOB_ICON_FOREGROUND_COLOR is an invalid string", () => {
|
describe("when BONOB_ICON_FOREGROUND_COLOR is an invalid string", () => {
|
||||||
it(`should blow up`, () => {
|
it(`should blow up`, () => {
|
||||||
process.env["BONOB_ICON_FOREGROUND_COLOR"] = "#dfasd";
|
process.env["BONOB_ICON_FOREGROUND_COLOR"] = "#dfasd";
|
||||||
expect(() => config()).toThrow("Invalid color specified for BONOB_ICON_FOREGROUND_COLOR")
|
expect(() => config()).toThrow(
|
||||||
|
"Invalid color specified for BONOB_ICON_FOREGROUND_COLOR"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -161,11 +163,75 @@ describe("config", () => {
|
|||||||
describe("when BONOB_ICON_BACKGROUND_COLOR is an invalid string", () => {
|
describe("when BONOB_ICON_BACKGROUND_COLOR is an invalid string", () => {
|
||||||
it(`should blow up`, () => {
|
it(`should blow up`, () => {
|
||||||
process.env["BONOB_ICON_BACKGROUND_COLOR"] = "#red";
|
process.env["BONOB_ICON_BACKGROUND_COLOR"] = "#red";
|
||||||
expect(() => config()).toThrow("Invalid color specified for BONOB_ICON_BACKGROUND_COLOR")
|
expect(() => config()).toThrow(
|
||||||
|
"Invalid color specified for BONOB_ICON_BACKGROUND_COLOR"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
describe("fontColor", () => {
|
||||||
|
describe("when BONOB_ICON_FONT_COLOR is not specified", () => {
|
||||||
|
it(`should default to undefined`, () => {
|
||||||
|
expect(config().icons.fontColor).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_COLOR is ''", () => {
|
||||||
|
it(`should default to undefined`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_COLOR"] = "";
|
||||||
|
expect(config().icons.fontColor).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_COLOR is specified", () => {
|
||||||
|
it(`should use it`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_COLOR"] = "pink";
|
||||||
|
expect(config().icons.fontColor).toEqual("pink");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_COLOR is an invalid string", () => {
|
||||||
|
it(`should blow up`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_COLOR"] = "#dfasd";
|
||||||
|
expect(() => config()).toThrow(
|
||||||
|
"Invalid color specified for BONOB_ICON_FONT_COLOR"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fontFamily", () => {
|
||||||
|
describe("when BONOB_ICON_FONT_FAMILY is not specified", () => {
|
||||||
|
it(`should default to undefined`, () => {
|
||||||
|
expect(config().icons.fontFamily).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_FAMILY is ''", () => {
|
||||||
|
it(`should default to undefined`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_FAMILY"] = "";
|
||||||
|
expect(config().icons.fontFamily).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_FAMILY is specified", () => {
|
||||||
|
it(`should use it`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_FAMILY"] = "helveta";
|
||||||
|
expect(config().icons.fontFamily).toEqual("helveta");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when BONOB_ICON_FONT_FAMILY is an invalid string", () => {
|
||||||
|
it(`should blow up`, () => {
|
||||||
|
process.env["BONOB_ICON_FONT_FAMILY"] = "#dfasd";
|
||||||
|
expect(() => config()).toThrow(
|
||||||
|
"Invalid color specified for BONOB_ICON_FONT_FAMILY"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("secret", () => {
|
describe("secret", () => {
|
||||||
it("should default to bonob", () => {
|
it("should default to bonob", () => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
ColorOverridingIcon,
|
ColorOverridingIcon,
|
||||||
HOLI_COLORS,
|
HOLI_COLORS,
|
||||||
Icon,
|
Icon,
|
||||||
|
iconForGenre,
|
||||||
makeFestive,
|
makeFestive,
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
Transformation,
|
Transformation,
|
||||||
@@ -16,17 +17,17 @@ describe("SvgIcon", () => {
|
|||||||
|
|
||||||
const svgIcon24 = `<?xml version="1.0" encoding="UTF-8"?>
|
const svgIcon24 = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const svgIcon128 = `<?xml version="1.0" encoding="UTF-8"?>
|
const svgIcon128 = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -47,9 +48,9 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -64,9 +65,9 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-21 -21 170 170">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-21 -21 170 170">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -91,10 +92,10 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<rect x="0" y="0" width="24" height="24" style="fill:red"/>
|
<rect x="0" y="0" width="24" height="24" fill="red"/>
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -110,10 +111,10 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
||||||
<rect x="-4" y="-4" width="36" height="36" style="fill:pink"/>
|
<rect x="-4" y="-4" width="36" height="36" fill="pink"/>
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -123,15 +124,13 @@ describe("SvgIcon", () => {
|
|||||||
describe("of undefined", () => {
|
describe("of undefined", () => {
|
||||||
it("should not do anything", () => {
|
it("should not do anything", () => {
|
||||||
expect(
|
expect(
|
||||||
new SvgIcon(svgIcon24)
|
new SvgIcon(svgIcon24).with({ backgroundColor: undefined }).toString()
|
||||||
.with({ backgroundColor: undefined })
|
|
||||||
.toString()
|
|
||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -148,10 +147,10 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<rect x="0" y="0" width="24" height="24" style="fill:red"/>
|
<rect x="0" y="0" width="24" height="24" fill="red"/>
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -167,9 +166,9 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="something1" style="fill:red"/>
|
<path d="path1" fill="red"/>
|
||||||
<path d="something2" style="fill:red"/>
|
<path d="path2" fill="none" stroke="red"/>
|
||||||
<path d="something3" style="fill:red"/>
|
<path d="path3" fill="red"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -185,9 +184,9 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
||||||
<path d="something1" style="fill:pink"/>
|
<path d="path1" fill="pink"/>
|
||||||
<path d="something2" style="fill:pink"/>
|
<path d="path2" fill="none" stroke="pink"/>
|
||||||
<path d="something3" style="fill:pink"/>
|
<path d="path3" fill="pink"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -197,15 +196,13 @@ describe("SvgIcon", () => {
|
|||||||
describe("of undefined", () => {
|
describe("of undefined", () => {
|
||||||
it("should not do anything", () => {
|
it("should not do anything", () => {
|
||||||
expect(
|
expect(
|
||||||
new SvgIcon(svgIcon24)
|
new SvgIcon(svgIcon24).with({ foregroundColor: undefined }).toString()
|
||||||
.with({ foregroundColor: undefined })
|
|
||||||
.toString()
|
|
||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="something1"/>
|
<path d="path1"/>
|
||||||
<path d="something2"/>
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
<path d="something3"/>
|
<path d="path3"/>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -222,9 +219,78 @@ describe("SvgIcon", () => {
|
|||||||
).toEqual(
|
).toEqual(
|
||||||
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="something1" style="fill:red"/>
|
<path d="path1" fill="red"/>
|
||||||
<path d="something2" style="fill:red"/>
|
<path d="path2" fill="none" stroke="red"/>
|
||||||
<path d="something3" style="fill:red"/>
|
<path d="path3" fill="red"/>
|
||||||
|
</svg>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with some text", () => {
|
||||||
|
describe("with no font color or style", () => {
|
||||||
|
describe("with no viewPort increase", () => {
|
||||||
|
it("should render the line", () => {
|
||||||
|
expect(
|
||||||
|
new SvgIcon(svgIcon24).with({ text: "hello" }).toString()
|
||||||
|
).toEqual(
|
||||||
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="path1" />
|
||||||
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
|
<path d="path3" />
|
||||||
|
<g font-size="6" font-weight="bold">
|
||||||
|
<text x="2" y="19">hello</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with a viewPort increase", () => {
|
||||||
|
it("should render the line", () => {
|
||||||
|
expect(
|
||||||
|
new SvgIcon(svgIcon24)
|
||||||
|
.with({ viewPortIncreasePercent: 50, text: "hello" })
|
||||||
|
.toString()
|
||||||
|
).toEqual(
|
||||||
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 32 32">
|
||||||
|
<path d="path1" />
|
||||||
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
|
<path d="path3" />
|
||||||
|
<g font-size="9" font-weight="bold">
|
||||||
|
<text x="-1" y="24">hello</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with no font color and style", () => {
|
||||||
|
it("should render the line", () => {
|
||||||
|
expect(
|
||||||
|
new SvgIcon(svgIcon24)
|
||||||
|
.with({
|
||||||
|
text: "hello world",
|
||||||
|
fontColor: "red",
|
||||||
|
fontFamily: "helvetica",
|
||||||
|
})
|
||||||
|
.toString()
|
||||||
|
).toEqual(
|
||||||
|
xmlTidy(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="path1" />
|
||||||
|
<path d="path2" fill="none" stroke="#000"/>
|
||||||
|
<path d="path3" />
|
||||||
|
<g font-size="6" font-weight="bold" font-family="helvetica" style="fill:red">
|
||||||
|
<text x="2" y="19">hello world</text>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
@@ -249,37 +315,92 @@ describe("ColorOverridingIcon", () => {
|
|||||||
const icon = new DummyIcon({
|
const icon = new DummyIcon({
|
||||||
backgroundColor: "black",
|
backgroundColor: "black",
|
||||||
foregroundColor: "black",
|
foregroundColor: "black",
|
||||||
|
fontColor: "black",
|
||||||
|
fontFamily: "plain",
|
||||||
});
|
});
|
||||||
const overriding = new ColorOverridingIcon(
|
|
||||||
icon,
|
|
||||||
() => true,
|
|
||||||
() => ({ backgroundColor: "blue", foregroundColor: "red" })
|
|
||||||
);
|
|
||||||
|
|
||||||
describe("with", () => {
|
describe("overriding some options", () => {
|
||||||
it("should be the with of the underlieing icon with the overriden colors", () => {
|
const overriding = new ColorOverridingIcon(
|
||||||
const result = overriding.with({
|
icon,
|
||||||
viewPortIncreasePercent: 99,
|
() => true,
|
||||||
backgroundColor: "shouldBeIgnored",
|
() => ({ backgroundColor: "blue", foregroundColor: "red" })
|
||||||
foregroundColor: "shouldBeIgnored",
|
);
|
||||||
}) as DummyIcon;
|
|
||||||
|
|
||||||
expect(result.transformation).toEqual({
|
describe("with", () => {
|
||||||
viewPortIncreasePercent: 99,
|
it("should be the with of the underlieing icon with the overriden colors", () => {
|
||||||
backgroundColor: "blue",
|
const result = overriding.with({
|
||||||
foregroundColor: "red",
|
viewPortIncreasePercent: 99,
|
||||||
|
backgroundColor: "shouldBeIgnored",
|
||||||
|
foregroundColor: "shouldBeIgnored",
|
||||||
|
}) as DummyIcon;
|
||||||
|
|
||||||
|
expect(result.transformation).toEqual({
|
||||||
|
viewPortIncreasePercent: 99,
|
||||||
|
backgroundColor: "blue",
|
||||||
|
foregroundColor: "red",
|
||||||
|
fontColor: "black",
|
||||||
|
fontFamily: "plain",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toString", () => {
|
||||||
|
it("should be the toString of the underlieing icon with the overriden colors", () => {
|
||||||
|
expect(overriding.toString()).toEqual(
|
||||||
|
new DummyIcon({
|
||||||
|
backgroundColor: "blue",
|
||||||
|
foregroundColor: "red",
|
||||||
|
fontColor: "black",
|
||||||
|
fontFamily: "plain",
|
||||||
|
}).toString()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("toString", () => {
|
describe("overriding all options", () => {
|
||||||
it("should be the toString of the underlieing icon with the overriden colors", () => {
|
const overriding = new ColorOverridingIcon(
|
||||||
expect(overriding.toString()).toEqual(
|
icon,
|
||||||
new DummyIcon({
|
() => true,
|
||||||
|
() => ({
|
||||||
|
backgroundColor: "blue",
|
||||||
|
foregroundColor: "red",
|
||||||
|
fontColor: "pink",
|
||||||
|
fontFamily: "fancy",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
describe("with", () => {
|
||||||
|
it("should be the with of the underlieing icon with the overriden colors", () => {
|
||||||
|
const result = overriding.with({
|
||||||
|
viewPortIncreasePercent: 99,
|
||||||
|
backgroundColor: "shouldBeIgnored",
|
||||||
|
foregroundColor: "shouldBeIgnored",
|
||||||
|
fontColor: "shouldBeIgnored",
|
||||||
|
fontFamily: "shouldBeIgnored",
|
||||||
|
}) as DummyIcon;
|
||||||
|
|
||||||
|
expect(result.transformation).toEqual({
|
||||||
|
viewPortIncreasePercent: 99,
|
||||||
backgroundColor: "blue",
|
backgroundColor: "blue",
|
||||||
foregroundColor: "red",
|
foregroundColor: "red",
|
||||||
}).toString()
|
fontColor: "pink",
|
||||||
);
|
fontFamily: "fancy",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toString", () => {
|
||||||
|
it("should be the toString of the underlieing icon with the overriden colors", () => {
|
||||||
|
expect(overriding.toString()).toEqual(
|
||||||
|
new DummyIcon({
|
||||||
|
backgroundColor: "blue",
|
||||||
|
foregroundColor: "red",
|
||||||
|
fontColor: "pink",
|
||||||
|
fontFamily: "fancy",
|
||||||
|
}).toString()
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -323,12 +444,13 @@ describe("makeFestive", () => {
|
|||||||
const icon = new DummyIcon({
|
const icon = new DummyIcon({
|
||||||
backgroundColor: "black",
|
backgroundColor: "black",
|
||||||
foregroundColor: "black",
|
foregroundColor: "black",
|
||||||
|
fontColor: "black",
|
||||||
});
|
});
|
||||||
let now = dayjs();
|
let now = dayjs();
|
||||||
|
|
||||||
const festiveIcon = makeFestive(icon, { now: () => now })
|
const festiveIcon = makeFestive(icon, { now: () => now });
|
||||||
|
|
||||||
describe("on a non special day", () => {
|
describe("on a day that isn't festive", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
now = dayjs("2022/10/12");
|
now = dayjs("2022/10/12");
|
||||||
});
|
});
|
||||||
@@ -338,12 +460,14 @@ describe("makeFestive", () => {
|
|||||||
viewPortIncreasePercent: 88,
|
viewPortIncreasePercent: 88,
|
||||||
backgroundColor: "shouldBeUsed",
|
backgroundColor: "shouldBeUsed",
|
||||||
foregroundColor: "shouldBeUsed",
|
foregroundColor: "shouldBeUsed",
|
||||||
|
fontColor: "shouldBeUsed",
|
||||||
}) as DummyIcon;
|
}) as DummyIcon;
|
||||||
|
|
||||||
expect(result.transformation).toEqual({
|
expect(result.transformation).toEqual({
|
||||||
viewPortIncreasePercent: 88,
|
viewPortIncreasePercent: 88,
|
||||||
backgroundColor: "shouldBeUsed",
|
backgroundColor: "shouldBeUsed",
|
||||||
foregroundColor: "shouldBeUsed",
|
foregroundColor: "shouldBeUsed",
|
||||||
|
fontColor: "shouldBeUsed",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -353,17 +477,19 @@ describe("makeFestive", () => {
|
|||||||
now = dayjs("2022/12/25");
|
now = dayjs("2022/12/25");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use the given colors", () => {
|
it("should use the christmas theme colors", () => {
|
||||||
const result = festiveIcon.with({
|
const result = festiveIcon.with({
|
||||||
viewPortIncreasePercent: 25,
|
viewPortIncreasePercent: 25,
|
||||||
backgroundColor: "shouldNotBeUsed",
|
backgroundColor: "shouldNotBeUsed",
|
||||||
foregroundColor: "shouldNotBeUsed",
|
foregroundColor: "shouldNotBeUsed",
|
||||||
|
fontColor: "shouldNotBeUsed",
|
||||||
}) as DummyIcon;
|
}) as DummyIcon;
|
||||||
|
|
||||||
expect(result.transformation).toEqual({
|
expect(result.transformation).toEqual({
|
||||||
viewPortIncreasePercent: 25,
|
viewPortIncreasePercent: 25,
|
||||||
backgroundColor: "green",
|
backgroundColor: "green",
|
||||||
foregroundColor: "red",
|
foregroundColor: "red",
|
||||||
|
fontColor: "white",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -378,12 +504,14 @@ describe("makeFestive", () => {
|
|||||||
viewPortIncreasePercent: 12,
|
viewPortIncreasePercent: 12,
|
||||||
backgroundColor: "shouldNotBeUsed",
|
backgroundColor: "shouldNotBeUsed",
|
||||||
foregroundColor: "shouldNotBeUsed",
|
foregroundColor: "shouldNotBeUsed",
|
||||||
|
fontColor: "shouldNotBeUsed",
|
||||||
}) as DummyIcon;
|
}) as DummyIcon;
|
||||||
|
|
||||||
expect(result.transformation).toEqual({
|
expect(result.transformation).toEqual({
|
||||||
viewPortIncreasePercent: 12,
|
viewPortIncreasePercent: 12,
|
||||||
backgroundColor: "orange",
|
backgroundColor: "orange",
|
||||||
foregroundColor: "black",
|
foregroundColor: "black",
|
||||||
|
fontColor: "orangered",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -398,12 +526,14 @@ describe("makeFestive", () => {
|
|||||||
viewPortIncreasePercent: 12,
|
viewPortIncreasePercent: 12,
|
||||||
backgroundColor: "shouldNotBeUsed",
|
backgroundColor: "shouldNotBeUsed",
|
||||||
foregroundColor: "shouldNotBeUsed",
|
foregroundColor: "shouldNotBeUsed",
|
||||||
|
fontColor: "shouldNotBeUsed",
|
||||||
}) as DummyIcon;
|
}) as DummyIcon;
|
||||||
|
|
||||||
expect(result.transformation).toEqual({
|
expect(result.transformation).toEqual({
|
||||||
viewPortIncreasePercent: 12,
|
viewPortIncreasePercent: 12,
|
||||||
backgroundColor: "red",
|
backgroundColor: "red",
|
||||||
foregroundColor: "yellow",
|
foregroundColor: "yellow",
|
||||||
|
fontColor: "crimson",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -418,12 +548,58 @@ describe("makeFestive", () => {
|
|||||||
viewPortIncreasePercent: 12,
|
viewPortIncreasePercent: 12,
|
||||||
backgroundColor: "shouldNotBeUsed",
|
backgroundColor: "shouldNotBeUsed",
|
||||||
foregroundColor: "shouldNotBeUsed",
|
foregroundColor: "shouldNotBeUsed",
|
||||||
|
fontColor: "shouldNotBeUsed",
|
||||||
}) as DummyIcon;
|
}) as DummyIcon;
|
||||||
|
|
||||||
expect(result.transformation.viewPortIncreasePercent).toEqual(12);
|
expect(result.transformation.viewPortIncreasePercent).toEqual(12);
|
||||||
expect(HOLI_COLORS.includes(result.transformation.backgroundColor!)).toEqual(true);
|
expect(
|
||||||
expect(HOLI_COLORS.includes(result.transformation.foregroundColor!)).toEqual(true);
|
HOLI_COLORS.includes(result.transformation.backgroundColor!)
|
||||||
expect(result.transformation.backgroundColor).not.toEqual(result.transformation.foregroundColor);
|
).toEqual(true);
|
||||||
|
expect(
|
||||||
|
HOLI_COLORS.includes(result.transformation.foregroundColor!)
|
||||||
|
).toEqual(true);
|
||||||
|
expect(HOLI_COLORS.includes(result.transformation.fontColor!)).toEqual(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(result.transformation.backgroundColor).not.toEqual(
|
||||||
|
result.transformation.foregroundColor
|
||||||
|
);
|
||||||
|
expect(result.transformation.backgroundColor).not.toEqual(
|
||||||
|
result.transformation.fontColor
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("iconForGenre", () => {
|
||||||
|
[
|
||||||
|
["Acid House", "mushroom"],
|
||||||
|
["African", "african"],
|
||||||
|
["Alternative Rock", "rock"],
|
||||||
|
["Americana", "americana"],
|
||||||
|
["Anti-Folk", "guitar"],
|
||||||
|
["Audio-Book", "book"],
|
||||||
|
["Australian Hip Hop", "oz"],
|
||||||
|
["Rap", "rap"],
|
||||||
|
["Hip Hop", "hipHop"],
|
||||||
|
["Hip-Hop", "hipHop"],
|
||||||
|
["Metal", "metal"],
|
||||||
|
["Horrorcore", "horror"],
|
||||||
|
["Punk", "punk"],
|
||||||
|
["blah", "music"],
|
||||||
|
].forEach(([genre, expected]) => {
|
||||||
|
describe(`a genre of ${genre}`, () => {
|
||||||
|
it(`should have an icon of ${expected}`, () => {
|
||||||
|
const name = iconForGenre(genre!)!;
|
||||||
|
expect(name).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`a genre of ${genre!.toLowerCase()}`, () => {
|
||||||
|
it(`should have an icon of ${expected}`, () => {
|
||||||
|
const name = iconForGenre(genre!)!;
|
||||||
|
expect(name).toEqual(expected);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1370,8 +1370,18 @@ describe("server", () => {
|
|||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200);
|
||||||
const svg = Buffer.from(response.body).toString();
|
const svg = Buffer.from(response.body).toString();
|
||||||
expect(svg).toContain(`fill:brightblue`);
|
expect(svg).toContain(`fill="brightblue"`);
|
||||||
expect(svg).toContain(`fill:brightpink`);
|
expect(svg).toContain(`fill="brightpink"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an icon with text if requested", async () => {
|
||||||
|
const response = await request(server(SystemClock)).get(
|
||||||
|
`/icon/${type}/size/180?text=foobar1000`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
const svg = Buffer.from(response.body).toString();
|
||||||
|
expect(svg).toContain(`foobar1000`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return a christmas icon on christmas day", async () => {
|
it("should return a christmas icon on christmas day", async () => {
|
||||||
@@ -1381,8 +1391,8 @@ describe("server", () => {
|
|||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200);
|
||||||
const svg = Buffer.from(response.body).toString();
|
const svg = Buffer.from(response.body).toString();
|
||||||
expect(svg).toContain(`fill:red`);
|
expect(svg).toContain(`fill="red"`);
|
||||||
expect(svg).toContain(`fill:green`);
|
expect(svg).toContain(`fill="green"`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import {
|
|||||||
import { AccessTokens } from "../src/access_tokens";
|
import { AccessTokens } from "../src/access_tokens";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import url from "../src/url_builder";
|
import url from "../src/url_builder";
|
||||||
|
import { iconForGenre } from "../src/icon";
|
||||||
|
|
||||||
const parseXML = (value: string) => new DOMParserImpl().parseFromString(value);
|
const parseXML = (value: string) => new DOMParserImpl().parseFromString(value);
|
||||||
|
|
||||||
@@ -939,6 +940,7 @@ describe("api", () => {
|
|||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre.id}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre.name,
|
title: genre.name,
|
||||||
|
albumArtURI: iconArtURI(bonobUrl, iconForGenre(genre.name), genre.name).href(),
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
total: expectedGenres.length,
|
total: expectedGenres.length,
|
||||||
@@ -960,6 +962,7 @@ describe("api", () => {
|
|||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre.id}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre.name,
|
title: genre.name,
|
||||||
|
albumArtURI: iconArtURI(bonobUrl, iconForGenre(genre.name), genre.name).href(),
|
||||||
})),
|
})),
|
||||||
index: 1,
|
index: 1,
|
||||||
total: expectedGenres.length,
|
total: expectedGenres.length,
|
||||||
|
|||||||
3
web/icons/Africa-48087.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M4.96 2.126l.613.416c.301.204.663.321.982.425.158.051.396.128.466.173l.035.023.01.006c.13.084.309.2.535.272C7.748 3.488 7.906 3.513 8.06 3.513c.339 0 .651-.12.881-.339.274.105.601.224.972.25.038.135.068.273.1.423.057.267.122.569.239.889.089.259.208.609.491.91.17.203.355.324.48.407l.031.021c.029.04.076.119.111.179.075.128.166.282.29.437.124.187.296.347.49.456-.088.076-.18.15-.278.222-.365.24-.64.498-.839.788L11.004 8.19l-.02.035-.027.047c-.217.377-.542.941-.423 1.627.034.285.108.529.174.745.017.055.034.11.05.166l-.022.011-.034.02c-.096.056-.938.57-.938 1.369 0 .095.01.182.027.262-.218.2-.351.476-.354.785-.104.14-.181.278-.247.397-.023.042-.046.085-.071.126-.071.037-.216.084-.395.12-.093-.214-.222-.361-.308-.457-.158-.228-.362-.396-.544-.545-.039-.032-.089-.073-.131-.109-.014-.118-.032-.235-.059-.34-.099-.385-.315-.667-.489-.894-.032-.041-.073-.095-.105-.14.02-.049.046-.108.068-.156.099-.221.234-.523.265-.894l.004-.043v-.043c0-.499-.127-.942-.395-1.371.057-.163.105-.375.093-.624C7.139 8.2 7.158 8.088 7.158 7.957c0-.008-.022-.643-.291-1.164C6.855 6.754 6.841 6.716 6.825 6.678 6.626 6.218 6.181 5.985 5.5 5.985c-.04 0-.09 0-.148 0-.586 0-1.988 0-2.326-.001C2.999 5.971 2.972 5.955 2.946 5.94L2.741 5.824C2.63 5.762 2.451 5.662 2.269 5.557c.006-.015.011-.03.016-.045.221-.641.094-1.106-.069-1.397.039-.033.081-.064.126-.094l.06-.034c.237-.13.73-.402.92-1.048.023-.09.036-.175.042-.252.069-.054.145-.12.219-.201.005 0 .01 0 .014 0 .419 0 .775-.15 1.034-.259C4.678 2.209 4.72 2.191 4.761 2.175 4.827 2.156 4.893 2.14 4.96 2.126M5.889 1C5.382 1.035 4.911 1.071 4.441 1.211c-.25.091-.553.26-.841.26-.046 0-.092-.004-.137-.014-.109-.035-.181-.105-.29-.105-.109 0-.217.035-.254.105-.036.071 0 .105 0 .176C2.883 1.913 2.448 1.984 2.376 2.23c-.036.141 0 .316-.036.457C2.268 2.932 2.014 3.038 1.833 3.144c-.326.211-.579.457-.76.808C1.036 4.022 1 4.093 1 4.162c0 .141.217.281.29.386C1.434 4.725 1.398 4.97 1.326 5.181c-.073.211-.217.386-.29.562C1 5.814 1 5.885 1 5.919c.036.035.073.07.109.106.326.246 1.145.686 1.326.792.157.091.341.18.505.182C3 7 5 7 5.5 7c0 0 .5 0 .375.133C6.02 7.238 6.142 7.817 6.142 7.957c0 .14-.073.246-.036.352.036.387-.349.597-.096.913.254.316.398.632.398 1.054-.036.422-.362.773-.362 1.194 0 .457.543.808.652 1.23.036.141.036.316.073.457.073.316.639.597.82.878.073.106.181.175.217.316 0 .071 0 .141 0 .211 0 .175.145.351.326.422C8.166 14.995 8.201 15 8.238 15c.09 0 .192-.025.294-.05.398-.035 1.123-.175 1.376-.527.158-.219.254-.492.434-.668.109-.105.217-.211.181-.351-.036-.071-.073-.106-.073-.141 0-.071.145-.106.217-.106.073 0 .145 0 .217 0 .181-.035.254-.246.217-.386-.036-.175-.217-.281-.29-.457-.036-.035-.036-.07-.036-.105 0-.141.254-.387.434-.492.217-.105.471-.211.579-.422.073-.175.073-.351 0-.527-.073-.352-.217-.668-.254-1.019-.073-.351.145-.703.326-1.019.145-.211.362-.387.579-.527C13.167 7.677 13.891 6.878 14 6c-.254.211-.688.272-1.05.306-.109 0-.217 0-.29-.035-.073-.035-.145-.106-.181-.175C12.262 5.85 12.153 5.498 11.9 5.288c-.145-.106-.29-.175-.398-.317-.145-.141-.217-.352-.29-.563-.217-.598-.217-1.09-.471-1.687-.073-.14-.145-.316-.29-.316-.073 0-.145 0-.254 0-.045.007-.091.009-.136.009-.456 0-.912-.296-1.373-.391C8.645 2.009 8.594 2 8.544 2c-.07 0-.138.017-.18.058C8.256 2.164 8.348 2.335 8.24 2.44 8.197 2.481 8.13 2.498 8.06 2.498c-.049 0-.101-.008-.146-.023C7.805 2.44 7.701 2.369 7.592 2.299 7.23 2.053 6.506 1.948 6.144 1.702c.073-.105.109-.211.145-.317.036-.105-.038-.28-.146-.35C6.07 1 5.961 1 5.889 1L5.889 1zM13.875 10c-.099.215-.232.465-.398.571-.132.071-.299.143-.365.285-.099.179 0 .393-.033.571 0 .071-.033.143-.066.215-.033.179 0 .393.066.571.033.107.099.25.199.285.066 0 .166-.035.232-.107.066-.071.132-.215.166-.321s.033-.215.033-.321c.033-.321.199-.643.266-.965.033-.215.033-.429 0-.643C13.942 10.071 13.909 10 13.875 10l.017.035c.009.009.017.018.017.035l-.017-.035C13.884 10.027 13.875 10.018 13.875 10L13.875 10z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
5
web/icons/Blues-113548.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M4,14c0,0.691,4.925,1,11,1s11-0.309,11-1"/>
|
||||||
|
<path d="M15 12c5.66 0 8-.588 8-.588S22.07 3 19.5 3 17.436 4 15 4s-1.991-1-4.5-1S7 11.412 7 11.412 9.34 12 15 12zM12 24h-2c-1.657 0-3-1.343-3-3v-1c0-1.105.895-2 2-2h3c1.105 0 2 .895 2 2v2C14 23.105 13.105 24 12 24zM16 22v-2c0-1.105.895-2 2-2h3c1.105 0 2 .895 2 2v1c0 1.657-1.343 3-3 3h-2C16.895 24 16 23.105 16 22z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M13 20L17 20"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 695 B |
4
web/icons/Book-453.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26">
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M24.79,9.537c-0.576,1.411,0,2.674,0,2.674L15,24L3,20.438c0,0-2-0.853-2-3.438c0-3.576,3.081-2.764,3.081-2.764"/>
|
||||||
|
<path d="M25.84,4.567c-0.119-0.346-0.412-0.603-0.768-0.681L12.629,1.107c-0.374-0.078-0.765,0.049-1.017,0.34 L1.266,12.963c-0.237,0.271-0.891,1.318-1.094,2.678c0.577-0.715,1.266-1.072,1.614-0.979l12.187,3.479 c0.09,0.025,0.182,0.036,0.271,0.036c0.311,0,0.611-0.138,0.812-0.384L25.661,5.578C25.893,5.295,25.961,4.912,25.84,4.567z M20.144,7.546L19.347,8.49c-0.222,0.262-0.735,0.392-1.145,0.295l-7.428-1.81c-0.409-0.098-0.563-0.392-0.345-0.65l0.799-0.949 c0.22-0.26,0.734-0.39,1.144-0.292L19.8,6.891C20.207,6.989,20.363,7.284,20.144,7.546z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 867 B |
5
web/icons/Classic-Music-11646.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M28.8,14.6l-3.6-7.2C23.9,4.7,21.1,3,18.1,3H9c-4.4,0-8,3.6-8,8v24h24h24v-8c0-4.4-3.6-8-8-8h-5.1C32.9,19,30.1,17.3,28.8,14.6z"/>
|
||||||
|
<path d="M20.3 43L20 35 23 35 22.7 43zM24.3 43L24 35 27 35 26.7 43zM28.3 43L28 35 31 35 30.7 43zM36.3 43L36 35 39 35 38.7 43zM40.3 43L40 35 43 35 42.7 43zM8.3 43L8 35 11 35 10.7 43zM12.3 43L12 35 15 35 14.7 43z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M49,35v10c0,1.1-0.9,2-2,2H3c-1.1,0-2-0.9-2-2V35H49z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 619 B |
1
web/icons/Comedy-2-599.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26"><path d="M24.992,10.084c1.614,1.665,1.204,4.715-3.591,5.736c0-2.102-3.689-2.916-8.379-2.916 c-5.028,0-8.594,0.773-8.594,2.974c-4.811-0.992-5.087-4.37-3.156-6.06c2.35-2.056,3.31,1.362,11.714,1.362 C20.644,11.181,22.415,7.426,24.992,10.084z"/><path d="M12.987 9.834c3.761 0 7.938-1.868 7.938-1.868v-1.23c0-3.273-2.066-6.586-7.919-6.586-6.125 0-7.92 3.414-7.92 6.586v1.299C5.086 8.034 9.389 9.834 12.987 9.834zM13 15.121c-.453 0-.863.164-1.192.428.141-.087.297-.15.475-.15.509 0 .92.412.92.921s-.411.92-.92.92-.921-.411-.921-.92c0-.178.063-.334.149-.475-.263.328-.427.74-.427 1.192 0 1.059.857 1.916 1.916 1.916s1.916-.857 1.916-1.916S14.059 15.121 13 15.121zM7.787 20.879c.196.123.42.277.607.406 1.081.745 2.562 1.766 4.641 1.766 2.086 0 3.606-.997 4.717-1.726.099-.064.208-.136.317-.206-.164.313-.425.699-.813 1.123-.934 1.013-2.532 2.096-4.221 2.096-1.847 0-3.561-1.207-4.466-2.239C8.137 21.605 7.9 21.175 7.787 20.879M7.438 19.076c-.315 0-.624.125-.938.433-1.682 1.646 2.266 6.429 6.535 6.429 4.266 0 8.055-4.937 6.531-6.429-.271-.267-.549-.376-.84-.376-1.336 0-2.958 2.318-5.691 2.318C10.327 21.451 8.813 19.076 7.438 19.076L7.438 19.076z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
4
web/icons/Electronic-Music-17745.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M5 19L27 19M8 14L15 14M12 19L12 23M9 19L9 23M20 19L20 23M23 19L23 23M17 19L17 23M5 9H27V25H5z"/>
|
||||||
|
<path d="M22.5 13A1.5 1.5 0 1 0 22.5 16 1.5 1.5 0 1 0 22.5 13zM18.5 13A1.5 1.5 0 1 0 18.5 16 1.5 1.5 0 1 0 18.5 13z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 362 B |
5
web/icons/Guitar-110433.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||||
|
<path d="M51.7 69.2A7 7 0 1 0 51.7 83.2A7 7 0 1 0 51.7 69.2Z"/>
|
||||||
|
<path d="M110.8,21.1l-4-4c-0.4-0.4-0.9-0.8-1.4-1c-2.6-1.4-5.6-0.7-7.4,1.3L90,26.4c-1,1.1-1.5,2.5-1.5,3.8L67.9,50.8c-3.1-2.2-6.7-3.4-10.4-3.4c-4.6,0-8.8,1.8-11.9,5c-0.7,0.7-1.3,1.4-1.8,2.1c-3.3,4.5-8.3,6.7-11.8,7.7c-4.3,1.2-8.2,3.5-11.4,6.7c-7.9,7.9-8.6,20.6-1.7,30.2c6.7,9.4,19,17.9,27.9,17.9h0c7.3,0,14-3.8,18.8-10.8c1.5-2.1,2.4-4.6,3.2-7c1.5-5.2,4.3-9.4,7.9-12.2c0.7-0.6,1.4-1.2,2.1-1.8c3.5-3.5,5.2-7.9,4.9-12.5c-0.1-1.6-1.2-4-2.6-6.4c-2.2-3.8-1.6-8.7,1.5-11.8l15.1-15.1c1.4,0,2.8-0.6,3.9-1.5l9.1-8.1c1.2-1.1,1.9-2.7,2-4.3C112.6,23.9,112,22.3,110.8,21.1z M71.5,77.9c-0.5,0.5-1,0.9-1.5,1.3c-4.7,3.6-8.2,8.9-10.1,15.4c-1,3.3-2.7,6.2-5.1,8.6c-3.2,3.2-6.9,4.8-11.1,4.8c-6.9,0-14.4-4.6-20-12.4c-5.2-7.2-4.7-16.7,1.1-22.5c2.4-2.4,5.5-4.2,8.8-5.1c6.4-1.8,11.6-5.2,15-9.9c0.4-0.5,0.8-1,1.3-1.5c2-2,4.7-3.1,7.6-3.1c2.5,0,5.1,0.8,7.2,2.4c5.9,4.4,9.6,9.7,10,14.3C74.8,73,73.8,75.6,71.5,77.9z M73.2,55.4c-0.2-0.2-0.5-0.5-0.7-0.7l18.8-18.8l0.7,0.7L73.2,55.4z M97.5,33.5C97.5,33.5,97.5,33.5,97.5,33.5l-3.1-3.1l8.1-9.1l4,4L97.5,33.5z"/>
|
||||||
|
<path d="M39,80.4c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2l8.5,8.5c0.6,0.6,1.4,0.9,2.1,0.9s1.5-0.3,2.1-0.9c1.2-1.2,1.2-3.1,0-4.2L39,80.4z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
6
web/icons/Hip-Hop Music-17757.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M6,19c0,1.725,0.818,7,3.318,7C11,26,12.5,25,16,25c3.455,0,5,1,6.682,1C25.136,26,26,20.725,26,19"/>
|
||||||
|
<path d="M17.818,6.136C17.818,6.773,17,6.455,16,6.455s-1.818,0.273-1.818-0.318C14.182,5.5,15,5,16,5S17.818,5.5,17.818,6.136z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M16,7C10.5,7,6,9.409,6,14.409c0,0.545,0,4,0,4.591c0,0,4.455-1,10-1c5.5,0,10,1,10,1c0-0.727,0-3.682,0-4.591C26,9.409,21.5,7,16,7z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M18 11.123C18 10.503 17.497 10 16.877 10h-1.754C14.503 10 14 10.503 14 11.123v0c0 .515.351.965.851 1.09l2.299.575c.5.125.851.574.851 1.09v0C18 14.497 17.497 15 16.877 15h-1.754C14.503 15 14 14.497 14 13.877M16 10L16 8M16 17L16 15"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 986 B |
6
web/icons/Horror-4387.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M9.1,12.1L0,20.9l2.5,2.6c0.2,0.2,4-0.6,5.7-2.3c1.7-1.7,3.6-6.3,3.6-6.3L9.1,12.1z"/>
|
||||||
|
<path d="M22.3,2.8c-0.6,2.7-2.6,7.3-8.7,13.5l-3.3-3.4l9.8-9.4C20.9,3.4,21.7,3.2,22.3,2.8 M24,0c0,0-2.1,2.1-4.4,2.1c0,0,0,0-0.1,0C15.5,6,8.4,12.9,8.4,12.9l5.3,5.4C25.3,7,24,0,24,0L24,0z"/>
|
||||||
|
<path d="M23.9,0.3c0,0.2,0.1,7.6-8.5,16.2c1-1.4,3-5.7,2.6-8.4c-0.2-1.3,0-2.2,1.5-1.3c2,1.2,1.2-1.3,0.8-3c-0.2-1.1,1-1.6,1.9-2.1C22.3,1.5,23.9,0.5,23.9,0.3z"/>
|
||||||
|
<path d="M18 13.2c0 0-1.7 1.8-1.7 2.3 1.5-1.8-1.2 1.7.9 2.5.6.2 1.4-.3 1.3-1.4-.1-1.2-1.5-1.7.3-4.3C19.7 11 18 13.2 18 13.2zM15.9 19.7c0 0-.9 1.5-.9 2.2 0 .5.2 1.1.8 1.1.6 0 .8-.5.8-1.1C16.7 21.3 15.9 19.7 15.9 19.7z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 758 B |
4
web/icons/Ice-Pop Yellow-94532.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||||
|
<path d="M17,21h-4v5c0,1.105,0.895,2,2,2h0c1.105,0,2-0.895,2-2V21z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M8 10v10c0 .552.448 1 1 1h12c.552 0 1-.448 1-1V10c0-3.866-3.134-7-7-7h0C11.134 3 8 6.134 8 10zM12 10c0-1.657 1.343-3 3-3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 387 B |
4
web/icons/Kangaroo-16730.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M5,38c9.729,0,8.167-17.084,12-17c-0.384,5.349,3.147,6.326,2.318,9.197c-0.554,1.918-3.321,3.219-3.318,4.734c0.021,9.363-3,8.264-3,10.224C13,45.791,13.525,46,14,46c1.896,0,4.454-4.814,5-8.021c0.912-5.358,7.467-3.904,9-13.979c0,0,3.192,2.212,5.677,0.153"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M1 35c0 0 1.027 3 4 3C15.001 38 6.699 8 22.991 8c5.301 0 9.484 6 12.009 6 3.309 0 4.445-4.962 4.445-4.962s-1.156-.832-1.39-2.044C37.802 5.68 38.493 4 38.493 4s1.495.673 2.26 2.072c.807 1.476 1.64 2.102 1.64 2.102s1.158.447 2.488 1.68c.945.876 1.524 1.958 1.524 1.958s1.946 1.978 2.397 2.654c.507.761-.231 2.029-.231 2.029l-2.518-.352c0 0-2.491.791-3.806-.669-2.336-.876-1.921 4.255-5.009 6.828-1.447 1.206-1.503 3.946-1.689 5.367-.181 1.387-1.749 3.015-3.136 2.473M42.425 3.988c1.501 2.068.345 3.182 1.282 4.769M35.061 20.143c-3.371 4.933.887 7.595-.426 9.263"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
5
web/icons/Metal-Music-17763.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M10,15V6c0-1.105,0.895-2,2-2h0c1.105,0,2,0.895,2,2v7"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M10,15v3.172l-2.586-2.586c-0.781-0.781-2.047-0.781-2.828,0c-0.781,0.781-0.781,2.047,0,2.828l6.791,6.772C12.421,26.226,13.779,27,15.375,27H20c3.314,0,6-2.686,6-6V7c0-1.105-0.895-2-2-2s-2,0.895-2,2v6"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M20 10L20 10c-1.105 0-2 .895-2 2v4c0 1.105.895 2 2 2h0c1.105 0 2-.895 2-2v-4C22 10.895 21.105 10 20 10zM16 10L16 10c-1.105 0-2 .895-2 2v4c0 1.105.895 2 2 2h0c1.105 0 2-.895 2-2v-4C18 10.895 17.105 10 16 10z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 765 B |
4
web/icons/Mushroom-63864.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" d="M2.5,8c0,3.038,2.462,0.5,5.5,0.5s5.5,2.538,5.5-0.5S11.038,2.5,8,2.5S2.5,4.962,2.5,8z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" d="M9.152,8.625C9.369,10.951,9.5,12.135,9.5,12.5c0,0.828-0.672,1-1.5,1s-1.5-0.172-1.5-1c0-0.365,0.131-1.549,0.348-3.875"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 443 B |
4
web/icons/Music-14097.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M10 24L10 8 26 5 26 21M10 12L26 9"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M23 18A3 3 0 1 0 23 24 3 3 0 1 0 23 18zM7 21A3 3 0 1 0 7 27 3 3 0 1 0 7 21z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 337 B |
5
web/icons/Music-Conductor-225.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M31,35v12c0,1.102-0.898,2-2,2h-8c-1.102,0-2-0.898-2-2V35"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M25 1A5 5 0 1 0 25 11 5 5 0 1 0 25 1zM46.381 13.616c-.824-.822-2.159-.822-2.985 0l-5.263 5.251-5.265-5.251C32.229 12.979 27.316 13 26.838 13L25 24l-1.838-11c-.478 0-5.391-.021-6.03.616l-5.265 5.251-5.263-5.251c-.826-.822-2.161-.822-2.985 0-.826.824-.826 2.156 0 2.979l6.756 6.739c.411.412.951.616 1.492.616.542 0 1.081-.204 1.494-.616L18 19v16h14V19l4.639 4.334c.413.412.951.616 1.493.616.54 0 1.081-.204 1.492-.616l6.756-6.739C47.206 15.772 47.206 14.44 46.381 13.616z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M44.109 13.109L36.308 5.308"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 980 B |
6
web/icons/Music-Record-102104.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 11A1 1 0 1 0 12 13A1 1 0 1 0 12 11Z"/>
|
||||||
|
<path d="M12,18c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S15.309,18,12,18z M12,8c-2.206,0-4,1.794-4,4s1.794,4,4,4s4-1.794,4-4S14.206,8,12,8z"/>
|
||||||
|
<path d="M12,22C6.486,22,2,17.514,2,12S6.486,2,12,2s10,4.486,10,10S17.514,22,12,22z M12,4c-4.411,0-8,3.589-8,8s3.589,8,8,8s8-3.589,8-8S16.411,4,12,4z"/>
|
||||||
|
<path d="M8.566 14.02C8.215 13.425 8 12.741 8 12c0-2.209 1.791-4 4-4 .741 0 1.425.215 2.02.566l4.304-4.304C16.599 2.85 14.397 2 12 2 6.486 2 2 6.486 2 12c0 2.397.85 4.599 2.262 6.324L8.566 14.02zM15.434 9.98C15.785 10.575 16 11.259 16 12c0 2.209-1.791 4-4 4-.741 0-1.425-.215-2.02-.566l-4.304 4.304C7.401 21.15 9.603 22 12 22c5.514 0 10-4.486 10-10 0-2.397-.85-4.599-2.262-6.324L15.434 9.98z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 822 B |
1
web/icons/Music-Record-3397.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 10.9A1.1 1.1 0 1 0 12 13.1 1.1 1.1 0 1 0 12 10.9zM7.7 14.5C7.3 13.8 7 12.9 7 12c0-2.8 2.2-5 5-5 .9 0 1.8.3 2.5.7L18 1.6c-1.8-1-3.8-1.6-6-1.6C5.4 0 0 5.4 0 12c0 2.2.6 4.3 1.6 6L7.7 14.5zM22.4 6l-6.1 3.5c.4.7.7 1.6.7 2.5 0 2.8-2.2 5-5 5-.9 0-1.8-.3-2.5-.7L6 22.4c1.8 1 3.8 1.6 6 1.6 6.6 0 12-5.4 12-12C24 9.8 23.4 7.7 22.4 6z"/><g><path d="M12,1.5c5.8,0,10.5,4.7,10.5,10.5S17.8,22.5,12,22.5S1.5,17.8,1.5,12S6.2,1.5,12,1.5 M12,17.5 c3,0,5.5-2.5,5.5-5.5S15,6.5,12,6.5S6.5,9,6.5,12S9,17.5,12,17.5 M12,0C5.4,0,0,5.4,0,12c0,6.6,5.4,12,12,12s12-5.4,12-12 C24,5.4,18.6,0,12,0L12,0z M12,16c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S14.2,16,12,16L12,16z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 723 B |
1
web/icons/Pills-112386.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><path d="M66.4,103.2c2.1,12,12.3,20.8,24.1,20.8c1.4,0,2.7-0.1,4.1-0.4c6.4-1.1,11.9-4.8,15.6-10.2c3.7-5.4,5-12,3.9-18.6C112,82.8,101.9,74,90,74c-1.4,0-2.7,0.1-4.1,0.4C72.8,76.7,64,89.7,66.4,103.2z M87,80.3c1-0.2,2-0.3,3.1-0.3c8.9,0,16.6,6.7,18.2,15.8c0.9,5-0.2,10.1-3,14.2c-2.8,4.1-6.9,6.8-11.7,7.7c-1,0.2-2,0.3-3.1,0.3c-8.9,0-16.6-6.7-18.2-15.8C70.5,91.9,77.1,82.1,87,80.3z"/><path d="M83.7 109.9c.5.4 1.2.6 1.8.6 1 0 2-.5 2.6-1.4l9.6-16.4c1-1.4.6-3.4-.8-4.4-.5-.4-1.2-.6-1.8-.6-1 0-2 .5-2.6 1.4l-9.6 16.4C81.9 106.9 82.3 108.9 83.7 109.9zM20.8 120.9L21 121c3 1.7 6.4 2.6 9.8 2.6 7 0 13.5-3.8 17-9.8l11.7-20.3c0 0 0 0 0 0 0 0 0 0 0 0l11.7-20.3c5.4-9.4 2.2-21.4-7.2-26.8l-.1-.1c-3-1.7-6.4-2.6-9.8-2.6-7 0-13.5 3.8-17 9.8L25.4 73.8c0 0 0 0 0 0 0 0 0 0 0 0L13.7 94.1C8.2 103.5 11.5 115.5 20.8 120.9zM42.3 56.4c2.4-4.2 6.9-6.8 11.8-6.8 2.4 0 4.7.6 6.8 1.8l.1.1c6.5 3.8 8.7 12.1 5 18.6L55.8 87.9 32.1 74.2 42.3 56.4zM18.8 97.1l10.2-17.7 17.4 10.1-10.3 17.9c-2.8 4.8-7.5 7.7-12.6 8.2C17.3 111.7 15.2 103.5 18.8 97.1z"/><path d="M50.4,56.6c-1.4-0.8-3.3-0.3-4.1,1.1l-7,12.1c-0.8,1.4-0.3,3.3,1.1,4.1c0.5,0.3,1,0.4,1.5,0.4c1,0,2-0.5,2.6-1.5l7-12.1C52.3,59.2,51.8,57.4,50.4,56.6z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
1
web/icons/Progressive-Rock-24862.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M17,28L36.8,8.1c0.4-0.4,0.8-0.6,1.3-0.6l1.1-0.1c0.5-0.1,1-0.3,1.4-0.7L43,4.3l-0.9-0.9l-5.9,1.1c-0.5,0.1-1,0.4-1.4,0.7L14.6,25.6L17,28z"/><path d="M35.3 2.1999999999999997A.6.6 0 1 0 35.3 3.4.6.6 0 1 0 35.3 2.1999999999999997zM37 1.7999999999999998A.6.6 0 1 0 37 3 .6.6 0 1 0 37 1.7999999999999998zM38.6 1.5A.6.6 0 1 0 38.6 2.7.6.6 0 1 0 38.6 1.5zM40.2 1.2000000000000002A.6.6 0 1 0 40.2 2.4.6.6 0 1 0 40.2 1.2000000000000002zM41.8 1A.6.6 0 1 0 41.8 2.2.6.6 0 1 0 41.8 1z"/><path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M22.4,34l18.4-18.4c0.4-0.4,0.8-0.6,1.3-0.6l1.1-0.1c0.5-0.1,1-0.3,1.4-0.7l2.4-2.4l-0.9-0.9L40.2,12c-0.5,0.1-1,0.4-1.4,0.7L20,31.6L22.4,34z"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M26.638,29.763L26.638,29.763L22.4,34L20,31.6l4.576-4.6c-0.51,0-1.329,0-1.576,0c-1.105,0-2-0.895-2-2c0-0.365,0-1,0-1l-0.008-0.013L17,28l-2.4-2.4l4.603-4.649l-0.03-0.05C18.359,19.751,17.017,19,15.5,19c-2.316,0-4.223,1.75-4.472,3.999C11.009,23.164,11,23.331,11,23.5V24c-0.1,1.6-1,3-3,3c-0.054,0-0.085,0.018-0.122,0.031C4.027,27.348,1,30.568,1,34.5c0,0.154,0.013,0.382,0.013,0.5c-0.136,2.987,1.018,6.287,3.887,9.1C7.389,46.589,10,48,13.5,48s5.747-2.347,6.4-3c2.3-2.3,1.868-4.153,3.1-6c2-3,6-2.625,6-6.5C29,30.729,27.944,29.763,26.638,29.763z"/><path d="M39.3 9.700000000000001A.6.6 0 1 0 39.3 10.9.6.6 0 1 0 39.3 9.700000000000001zM41 9.3A.6.6 0 1 0 41 10.5.6.6 0 1 0 41 9.3zM42.6 9A.6.6 0 1 0 42.6 10.2.6.6 0 1 0 42.6 9zM44.2 8.700000000000001A.6.6 0 1 0 44.2 9.9.6.6 0 1 0 44.2 8.700000000000001zM45.8 8.5A.6.6 0 1 0 45.8 9.7.6.6 0 1 0 45.8 8.5z"/><path d="M16.284 34.887H17.715V39.113H16.284z" transform="rotate(-45.001 17 37)"/><path d="M10.284 28.887H11.715V33.113H10.284z" transform="rotate(-45.001 11 31)"/><path d="M16 42A1 1 0 1 0 16 44 1 1 0 1 0 16 42zM12 43A1 1 0 1 0 12 45 1 1 0 1 0 12 43z"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
6
web/icons/Punk-40450.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path d="M35.5 27A1.5 1.5 0 1 0 35.5 30A1.5 1.5 0 1 0 35.5 27Z"/>
|
||||||
|
<path d="M47.727,10.313c-0.312-0.329-0.805-0.408-1.203-0.192L36,15.821V3c0-0.458-0.312-0.857-0.755-0.97C34.8,1.919,34.336,2.122,34.12,2.526L28,13.891L21.88,2.526c-0.217-0.404-0.679-0.607-1.125-0.496C20.312,2.143,20,2.542,20,3v12.83L7.472,9.119C7.06,8.897,6.548,8.993,6.241,9.349C5.936,9.705,5.918,10.225,6.2,10.6l7.986,10.649L1.858,23.01c-0.451,0.064-0.801,0.425-0.852,0.877c-0.051,0.452,0.209,0.882,0.635,1.046l11.394,4.382l-9.668,7.911c-0.349,0.286-0.463,0.769-0.28,1.181C3.249,38.771,3.61,39,4,39c0.05,0,0.102-0.004,0.152-0.012l12.685-1.951C15.894,35.111,15,33.108,15,29c0-6.118,3.267-13,13-13c6.638,0,10.512,3.723,11.643,8.759c0.08-0.068,0.153-0.144,0.209-0.235l8-13C48.089,11.139,48.038,10.642,47.727,10.313z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M34,48v-3c0,0,1.529,0,4,0c1.954,0,3-1.794,3-3c0-0.909,0-7,0-7h3c0.892,0,1.16-0.895,0.812-1.521L40,28c0-6.599-4-12-12-12c-9.733,0-13,6.882-13,13c0,8.69,4,7.963,4,17v2"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M25,37L25,37c-2.2,0-4-1.8-4-4v-1c0-1.65,1.35-3,3-3l0,0c0,0,2-0.042,2,2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
12
web/icons/Rap-24851.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path d="M14,44l1.023,0.606C15.262,41.481,18.295,39,22,39c3.86,0,7,2.691,7,6c0,0.091-0.011,0.18-0.016,0.271L30,45l0.959-0.719C30.548,40.206,26.689,37,22,37c-4.238,0-7.793,2.621-8.743,6.134L14,44z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M36 21v5c0 0 3 .304 3 4 0 1.99-1.092 3.805-3 4M9 34c-1.908-.195-3-2.01-3-4 0-3.696 3-4 3-4v-5"/>
|
||||||
|
<path d="M38 21.625c0 0 .77 1.116 1.263 5.447C37.706 25.794 36 26.48 36 26.48l-.3-4.855H38zM7.263 21.625c0 0-.77 1.116-1.263 5.447 1.557-1.278 3.263-.592 3.263-.592l.3-4.855H7.263z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.066" d="M17.95,3h9.099c5.095,0,9.376,3.831,9.939,8.896L38,21H7l1.012-9.104C8.574,6.831,12.855,3,17.95,3z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.066" d="M22,17h-8c-1.1,0-2-0.9-2-2v0c0-1.1,0.9-2,2-2h8c1.1,0,2,0.9,2,2v0C24,16.1,23.1,17,22,17z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.066" d="M38,21c0,0,4.107,3.36,6,5c3.031,2.625,7.229,8-7.5,8"/>
|
||||||
|
<path d="M19,46c0,0,1.719-3,3-3s3,3,3,3H19z"/>
|
||||||
|
<path d="M25,47h-6c-0.356,0-0.687-0.19-0.865-0.499s-0.18-0.688-0.002-0.998C18.736,44.449,20.32,42,22,42s3.264,2.449,3.868,3.503c0.177,0.31,0.176,0.689-0.002,0.998S25.356,47,25,47z M20.897,45H23.1c-0.457-0.56-0.888-0.961-1.117-1.001C21.773,44.039,21.351,44.44,20.897,45z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M36,34c0,7.18-6.044,13-13.5,13S9,41.18,9,34"/>
|
||||||
|
<path d="M29,24.007c-0.643,0-1.256-0.014-1.821,0h-9.353c-0.566-0.014-1.181,0-1.826,0c-2.761,0-5-0.279-5,2.664C11,29.615,13.239,32,16,32c2.53,0,4.581-2.016,4.912-4.617c0.006-0.082,0.08-0.383,0.08-0.383c0.207-0.581,0.756-1,1.408-1h0.2c0.652,0,1.202,0.419,1.408,1c0,0,0.074,0.301,0.08,0.383C24.419,29.984,26.47,32,29,32c2.761,0,5-2.385,5-5.329C34,23.727,31.761,24.007,29,24.007z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
10
web/icons/Reggae-24843.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path d="M31.908 28C31.964 27.843 32 27.676 32 27.5c0-.828-.672-1.5-1.5-1.5S29 26.672 29 27.5c0 .176.036.343.092.5H31.908zM21.908 28C21.964 27.843 22 27.676 22 27.5c0-.828-.672-1.5-1.5-1.5S19 26.672 19 27.5c0 .176.036.343.092.5H21.908z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M36.027 23.729C36.653 26.565 37 29.701 37 33c0 4.398-.93 8.506-2 12M38.745 25.407C40.12 27.407 41 32.744 41 36c0 5.029-.771 8.602-2.03 12M44.705 45C44.897 43.584 45 42.071 45 40.5c0-8.008-2.25-14.5-4-14.5"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M41,26c-3.908,0-7.25-6-15.5-6S13.908,26,10,26c-2.792,0-4-2.131-4-6.5C6,9.835,14.73,2,25.5,2S45,9.835,45,19.5C45,23.869,43.792,26,41,26c-3.908,0-7.25-6-15.5-6S13.908,26,10,26"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M25.5,20c-8.25,0-11.592,6-15.5,6c-2.792,0-4-2.131-4-6.5c0-1.391,0.2-2.737,0.541-4.034C10.54,9.776,17.534,6,25.5,6s14.96,3.776,18.959,9.466C44.8,16.763,45,18.109,45,19.5c0,4.369-1.208,6.5-4,6.5C37.092,26,33.75,20,25.5,20z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M25.5,12c8.086,0,15.216,3.563,19.444,8.987C44.682,24.351,43.453,26,41,26c-3.908,0-7.25-6-15.5-6S13.908,26,10,26c-2.453,0-3.682-1.649-3.944-5.013C10.284,15.563,17.414,12,25.5,12s15.216,3.563,19.444,8.987C44.682,24.351,43.453,26,41,26c-3.908,0-7.25-6-15.5-6S13.908,26,10,26c-2.453,0-3.682-1.649-3.944-5.013C10.284,15.563,17.414,12,25.5,12z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M25.5 20c-4.493 0-7.526 1.779-9.992 3.4C15.181 24.85 15 26.394 15 28c0 8.284 4.701 15 10.5 15S36 36.284 36 28c0-1.606-.181-3.15-.508-4.6C33.026 21.779 29.993 20 25.5 20zM27 35L30 36M24 35L21 36"/>
|
||||||
|
<path d="M32.409,39.793c-0.116-0.21-0.304-0.35-0.512-0.432L32,39l-0.376,0.301c-0.193-0.018-0.392,0.001-0.574,0.102c-0.729,0.403-1.563,0.718-2.346,0.957c-0.767,0.235-1.615-0.022-2.106-0.657C26.267,39.274,25.894,39,25.5,39s-0.767,0.274-1.098,0.703c-0.49,0.635-1.338,0.892-2.106,0.657c-0.783-0.24-1.617-0.554-2.346-0.957c-0.183-0.101-0.381-0.12-0.574-0.102L19,39l0.103,0.361c-0.208,0.082-0.395,0.221-0.512,0.432c-0.268,0.483-0.092,1.092,0.391,1.359c0.239,0.132,0.487,0.253,0.738,0.369l1.391,2.123C21.666,44.49,22.609,45,23.621,45h3.758c1.011,0,1.955-0.51,2.509-1.356l1.391-2.123c0.251-0.116,0.499-0.237,0.738-0.369C32.501,40.885,32.676,40.276,32.409,39.793z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M16 45c-1.07-3.494-2-7.602-2-12 0-3.299.347-6.435.973-9.271M12.03 48C10.771 44.602 10 41.029 10 36c0-3.256.88-8.593 2.255-10.593M10 26c-1.75 0-4 6.492-4 14.5 0 1.571.103 3.084.295 4.5"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
7
web/icons/Rock-Music-11007.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M21.4,31L42.8,9.6c0.4-0.4,0.8-0.6,1.3-0.6l1.1-0.1c0.5-0.1,1-0.3,1.4-0.7L49,5.8l-0.9-0.9L42.2,6c-0.5,0.1-1,0.4-1.4,0.7L19,28.6L21.4,31z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M23.1,24.2c0.2-1.3,0.1-2.5-1.1-3.7c-1.7-1.7-4.7-1.8-6.6,0l0,0c-1.1,1.1-1.6,2.4-1.7,3.5c-0.1,1.6-1.5,2.8-3.1,2.7c-2.1-0.1-4.7,0.3-6.7,2.3l0,0C0,33-0.3,39,4.9,44.1c5.2,5.2,11.1,4.8,15,0.9c2.3-2.3,2.3-4.4,2.6-6.6c0.5-4.1,6.5-2.3,6.5-6.8c0-1.9-4.6,1.2-5.6-2.6"/>
|
||||||
|
<path d="M13.4 32.1H19.6V34.2H13.4z" transform="rotate(-134.999 16.537 33.103)"/>
|
||||||
|
<path d="M11.5 34.1H13.6V40.300000000000004H11.5z" transform="rotate(134.999 12.48 37.159)"/>
|
||||||
|
<path d="M18 41A1 1 0 1 0 18 43 1 1 0 1 0 18 41zM15 43A1 1 0 1 0 15 45 1 1 0 1 0 15 43zM11 43A1 1 0 1 0 11 45 1 1 0 1 0 11 43zM41.3 3.6999999999999997A.6.6 0 1 0 41.3 4.8999999999999995.6.6 0 1 0 41.3 3.6999999999999997zM43 3.3A.6.6 0 1 0 43 4.5.6.6 0 1 0 43 3.3zM44.6 3A.6.6 0 1 0 44.6 4.2.6.6 0 1 0 44.6 3zM46.2 2.6999999999999997A.6.6 0 1 0 46.2 3.9.6.6 0 1 0 46.2 2.6999999999999997zM47.8 2.5A.6.6 0 1 0 47.8 3.7.6.6 0 1 0 47.8 2.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
5
web/icons/Trumpet-17823.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M12 18L24.154 18 28 23 29 23 29 10 28 10 24.154 15 12.154 15"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2" d="M21.417 15H12c-2.209 0-4 1.791-4 4v0c0 2.209 1.791 4 4 4h6.5c1.381 0 2.5-1.119 2.5-2.5v0c0-1.381-1.119-2.5-2.5-2.5M3 19L3 15M12 15L12 12M15 15L15 12M18 15L18 12M12 25L12 20M15 25L15 20M18 25L18 20"/>
|
||||||
|
<path d="M10 19h3v-2h-1C10.897 17 10 17.897 10 19zM6.812 16H3v2h3.09C6.212 17.282 6.459 16.608 6.812 16z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 595 B |
10
web/icons/US-Capitol-104805.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||||
|
<path d="M9 11c0-3 2.7-6.373 6-6.373S21 8 21 11H9zM13.5 3c0-.7.7-1.5 1.5-1.5s1.5.8 1.5 1.5H13.5z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M8 14L22 14"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M9 14L9 20M13 14L13 20M17 14L17 20M21 14L21 20"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M6 20L24 20"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M7 21L23 21"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M6 26L24 26"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M7 20L7 27M11 20L11 27M15 20L15 27M19 20L19 27M23 20L23 27"/>
|
||||||
|
<path d="M15.322,2h-0.644C14.304,2,14,2.304,14,2.678V6h2V2.678C16,2.304,15.696,2,15.322,2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
3
web/icons/blank.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<g/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 76 B |
@@ -28,6 +28,185 @@
|
|||||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-12.5c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5 4.5-2.01 4.5-4.5-2.01-4.5-4.5-4.5zm0 5.5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"></path>
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-12.5c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5 4.5-2.01 4.5-4.5-2.01-4.5-4.5-4.5zm0 5.5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
hack 4<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" data-testid="icon">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<text style="fill:red;">Several lines:
|
||||||
|
<tspan >First line.</tspan>
|
||||||
|
<tspan >Second line.</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 5<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="12">
|
||||||
|
<text x="0%" y="45%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar1]]></text>
|
||||||
|
<text x="0%" y="90%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar2]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 5 (3 lines)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="8">
|
||||||
|
<text x="0%" y="30%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar1]]></text>
|
||||||
|
<text x="0%" y="60%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar2]]></text>
|
||||||
|
<text x="0%" y="90%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar3]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 5 (4 lines)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="6">
|
||||||
|
<text x="0%" y="22%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar1]]></text>
|
||||||
|
<text x="0%" y="45%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar2]]></text>
|
||||||
|
<text x="0%" y="67%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar3]]></text>
|
||||||
|
<text x="0%" y="90%" textLength="24" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar4]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 5 (4 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="5">
|
||||||
|
<text x="2" y="7" textLength="20" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar1]]></text>
|
||||||
|
<text x="2" y="12" textLength="20" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar2]]></text>
|
||||||
|
<text x="2" y="17" textLength="20" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar3]]></text>
|
||||||
|
<text x="2" y="22" textLength="20" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar4]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6a (4 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="20">
|
||||||
|
<text x="10" y="30" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar1]]></text>
|
||||||
|
<text x="10" y="50" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar2]]></text>
|
||||||
|
<text x="10" y="70" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar3]]></text>
|
||||||
|
<text x="10" y="90" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[foobar4]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6b 1 line<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="50" font-weight="bold">
|
||||||
|
<text x="10" y="60" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[psy]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6b 1 line %<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="50" font-weight="bold">
|
||||||
|
<text x="10" y="60" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[psy]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6b 2 lines<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="30" font-weight="bold">
|
||||||
|
<text x="10" y="40" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[psy]]></text>
|
||||||
|
<text x="10" y="80" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[trance]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6b2<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="30" font-weight="bold">
|
||||||
|
<text x="10" y="40" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[alt]]></text>
|
||||||
|
<text x="10" y="80" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[pop]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6b3<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="30" font-weight="bold">
|
||||||
|
<text x="10" y="40" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[oz]]></text>
|
||||||
|
<text x="10" y="80" textLength="80" lengthAdjust="spacingAndGlyphs"><![CDATA[rock]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6c (3 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="16" font-weight="bold" style="fill:pink">
|
||||||
|
<text x="20" y="26" textLength="60" lengthAdjust="spacingAndGlyphs"><![CDATA[oz]]></text>
|
||||||
|
<text x="20" y="52" textLength="60" lengthAdjust="spacingAndGlyphs"><![CDATA[hip]]></text>
|
||||||
|
<text x="20" y="78" textLength="60" lengthAdjust="spacingAndGlyphs"><![CDATA[hop]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6c with an embedded svg (1 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<svg x="20" y="20" width="60" height="60" viewBox="0 0 100 100">
|
||||||
|
<g font-family="helvetica" font-size="20" font-weight="bold">
|
||||||
|
<text x="0" y="100" textLength="100" lengthAdjust="spacingAndGlyphs"><![CDATA[oz]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6c with an embedded svg (3 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<svg x="10" y="10" width="80" height="80" viewBox="0 0 100 100">
|
||||||
|
<g font-family="helvetica" font-size="16" font-weight="bold">
|
||||||
|
<text x="0" y="26" textLength="100" lengthAdjust="spacingAndGlyphs"><![CDATA[oz]]></text>
|
||||||
|
<text x="0" y="52" textLength="100" lengthAdjust="spacingAndGlyphs"><![CDATA[hip]]></text>
|
||||||
|
<text x="0" y="78" textLength="100" lengthAdjust="spacingAndGlyphs"><![CDATA[hop]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6c% (3 lines with viewBox enlarge)<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" />
|
||||||
|
<g font-family="helvetica" font-size="16" font-weight="bold">
|
||||||
|
<text x="10%" y="26%" textLength="80%" lengthAdjust="spacingAndGlyphs"><![CDATA[oz]]></text>
|
||||||
|
<text x="10%" y="52%" textLength="80%" lengthAdjust="spacingAndGlyphs"><![CDATA[hip]]></text>
|
||||||
|
<text x="10%" y="78%" textLength="80%" lengthAdjust="spacingAndGlyphs"><![CDATA[hop]]></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
hack 6<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<!-- <rect x="0" y="0" width="100%" height="100%" style="fill:lightgrey" /> -->
|
||||||
|
<g font-face="sans-serif" font-size="10">
|
||||||
|
<text y="100%">
|
||||||
|
<tspan x="0" y="12" textLength="24" lengthAdjust="spacingAndGlyphs">First line.</tspan>
|
||||||
|
<tspan x="0" y="24" textLength="24" lengthAdjust="spacingAndGlyphs">Second line.</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
mushroom<br>
|
||||||
|
<div style="width:100px; height:100px; border: 1px; border-style: solid;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" d="M2.5,8c0,3.038,2.462,0.5,5.5,0.5s5.5,2.538,5.5-0.5S11.038,2.5,8,2.5S2.5,4.962,2.5,8z"/>
|
||||||
|
<path fill="none" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10" d="M9.152,8.625C9.369,10.951,9.5,12.135,9.5,12.5c0,0.828-0.672,1-1.5,1s-1.5-0.172-1.5-1c0-0.365,0.131-1.549,0.348-3.875"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||