Compare commits

..

No commits in common. "main" and "10.0.0" have entirely different histories.
main ... 10.0.0

30 changed files with 1280 additions and 3315 deletions

View File

@ -66,5 +66,3 @@ Example manifest:
## Development ## Development
Run `script/develop`. This starts a server. Open it on http://localhost:5001. Run `script/develop`. This starts a server. Open it on http://localhost:5001.
[![ESPHome - A project from the Open Home Foundation](https://www.openhomefoundation.org/badges/esphome.png)](https://www.openhomefoundation.org/)

View File

@ -281,30 +281,6 @@
</div> </div>
<div class="name">OpenEpaperLink</div> <div class="name">OpenEpaperLink</div>
</a> </a>
<a href="https://openspool.io" target="_blank" class="project">
<div class="logo">
<img src="static/logos/openspool.png" alt="OpenSpool logo" />
</div>
<div class="name">OpenSpool</div>
</a>
<a href="https://usetrmnl.com/flash" target="_blank" class="project">
<div class="logo">
<img src="static/logos/trmnl.png" alt="TRMNL logo" />
</div>
<div class="name">TRMNL</div>
</a>
<a href="https://nspanelmanager.com" target="_blank" class="project">
<div class="logo">
<img src="static/logos/nspanelmanager.svg" alt="NSPanelManager logo" />
</div>
<div class="name">NSPanel Manager</div>
</a>
<a href="https://github.com/blak3r/treadspan" target="_blank" class="project">
<div class="logo">
<img src="static/logos/treadspan.png" alt="Treadspan logo" />
</div>
<div class="name">TreadSpan</div>
</a>
</div> </div>
<h2>How it works</h2> <h2>How it works</h2>
@ -552,35 +528,6 @@ button.overrides = {
};</pre };</pre
> >
<h4>Generating a manifest dynamically &amp; version management</h4>
<p>
Alternatively to a static manifest JSON file, you can generate a Blob URL of a JSON object using <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static"><code>URL.createObjectURL</code></a> to use as the manifest url. This can be useful in situations where you have many firmware bin files, e.g. previous software versions, where you don't want to have a different static manifest json file for each bin. If you are hosting on github.io, this can be paired nicely with <a href="https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#get-contents">github's REST API</a> to view all the bin files inside a folder.
</p>
<pre>
const manifest = {
"name": name,
"version": version,
"funding_url": funding_url,
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP32",
"improv": false,
"parts": [
{ "path": dependenciesDir+"bootloader.bin", "offset": 4096 },
{ "path": dependenciesDir+"partitions.bin", "offset": 32768 },
{ "path": dependenciesDir+"boot_app0.bin", "offset": 57344 },
{ "path": firmwareFile, "offset": 65536 }
]
}
]
}
const json = JSON.stringify(manifest);
const blob = new Blob([json], {type: "application/json"});
document.querySelector("esp-web-install-button").manifest = URL.createObjectURL(blob);
</pre>
<h3 id="customize">Customizing the look and feel</h3> <h3 id="customize">Customizing the look and feel</h3>
<p> <p>
You can change the colors of the default UI elements with CSS custom You can change the colors of the default UI elements with CSS custom
@ -634,9 +581,7 @@ document.querySelector("esp-web-install-button").manifest = URL.createObjectURL(
<div class="footer"> <div class="footer">
<div class="initiative"> <div class="initiative">
ESP Web Tools is a project by ESP Web Tools is a project by
<a href="https://esphome.io">ESPHome</a>, <a href="https://esphome.io">ESPHome</a>.<br />
<a href="https://www.openhomefoundation.org">Open Home Foundation</a
>.<br />
Development is funded by Development is funded by
<a href="https://www.nabucasa.com">Nabu Casa</a>. <a href="https://www.nabucasa.com">Nabu Casa</a>.
</div> </div>

4157
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "esp-web-tools", "name": "esp-web-tools",
"version": "10.1.1", "version": "10.0.1",
"description": "Web tools for ESP devices", "description": "Web tools for ESP devices",
"main": "dist/install-button.js", "main": "dist/install-button.js",
"repository": "https://github.com/esphome/esp-web-tools", "repository": "https://github.com/esphome/esp-web-tools",
@ -10,25 +10,26 @@
"prepublishOnly": "script/build" "prepublishOnly": "script/build"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.26.0", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-transform-logical-assignment-operators": "^7.23.4",
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2", "@rollup/plugin-typescript": "^11.1.6",
"@types/w3c-web-serial": "^1.0.7", "@types/w3c-web-serial": "^1.0.6",
"prettier": "^3.4.2", "prettier": "^3.2.5",
"rollup": "^4.29.1", "rollup": "^4.10.0",
"serve": "^14.2.4", "serve": "^14.2.1",
"typescript": "^5.7.2" "typescript": "^5.3.3"
}, },
"dependencies": { "dependencies": {
"@material/web": "^2.2.0", "@material/web": "^1.2.0",
"esptool-js": "^0.5.3", "esptool-js": "^0.4.1",
"improv-wifi-serial-sdk": "^2.5.0", "improv-wifi-serial-sdk": "^2.5.0",
"lit": "^3.2.1", "lit": "^3.1.2",
"pako": "^2.1.0", "pako": "^2.1.0",
"tslib": "^2.8.1" "tslib": "^2.6.2"
} }
} }

View File

@ -20,16 +20,9 @@ const config = {
}), }),
babel({ babel({
babelHelpers: "bundled", babelHelpers: "bundled",
presets: [ plugins: [
[ "@babel/plugin-proposal-class-properties",
"@babel/preset-env", "@babel/plugin-transform-logical-assignment-operators",
{
targets: {
// We use unpkg as CDN and it doesn't bundle modern syntax
chrome: "84",
},
},
],
], ],
}), }),
json(), json(),

View File

@ -1,10 +1,6 @@
# Stop on errors # Stop on errors
set -e set -e
if [ -z "$PORT" ]; then
PORT=5001
fi
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
rm -rf dist rm -rf dist
@ -13,8 +9,8 @@ rm -rf dist
trap "kill 0" EXIT trap "kill 0" EXIT
# Run tsc once as rollup expects those files # Run tsc once as rollup expects those files
npm exec -- tsc || true tsc || true
npm exec -- serve -p "$PORT" & npm exec -- serve -p 5001 &
npm exec -- tsc --watch & npm exec -- tsc --watch &
npm exec -- rollup -c --watch npm exec -- rollup -c --watch

View File

@ -1,5 +1,5 @@
import { Checkbox } from "@material/web/checkbox/internal/checkbox.js"; import { Checkbox } from "@material/web/checkbox/internal/checkbox.js";
import { styles } from "@material/web/checkbox/internal/checkbox-styles.js"; import { styles } from "@material/web/checkbox/internal/checkbox-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,5 +1,5 @@
import { CircularProgress } from "@material/web/progress/internal/circular-progress.js"; import { CircularProgress } from "@material/web/progress/internal/circular-progress.js";
import { styles } from "@material/web/progress/internal/circular-progress-styles.js"; import { styles } from "@material/web/progress/internal/circular-progress-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,5 +1,5 @@
import { Dialog } from "@material/web/dialog/internal/dialog.js"; import { Dialog } from "@material/web/dialog/internal/dialog.js";
import { styles } from "@material/web/dialog/internal/dialog-styles.js"; import { styles } from "@material/web/dialog/internal/dialog-styles.css";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,5 +1,5 @@
import { Divider } from "@material/web/divider/internal/divider.js"; import { Divider } from "@material/web/divider/internal/divider.js";
import { styles } from "@material/web/divider/internal/divider-styles.js"; import { styles } from "@material/web/divider/internal/divider-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,6 +1,6 @@
import { FilledSelect } from "@material/web/select/internal/filled-select.js"; import { FilledSelect } from "@material/web/select/internal/filled-select.js";
import { styles } from "@material/web/select/internal/filled-select-styles.js"; import { styles } from "@material/web/select/internal/filled-select-styles.css.js";
import { styles as sharedStyles } from "@material/web/select/internal/shared-styles.js"; import { styles as sharedStyles } from "@material/web/select/internal/shared-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,6 +1,6 @@
import { styles as filledStyles } from "@material/web/textfield/internal/filled-styles.js"; import { styles as filledStyles } from "@material/web/textfield/internal/filled-styles.css.js";
import { FilledTextField } from "@material/web/textfield/internal/filled-text-field.js"; import { FilledTextField } from "@material/web/textfield/internal/filled-text-field.js";
import { styles as sharedStyles } from "@material/web/textfield/internal/shared-styles.js"; import { styles as sharedStyles } from "@material/web/textfield/internal/shared-styles.css.js";
import { literal } from "lit/static-html.js"; import { literal } from "lit/static-html.js";
declare global { declare global {

View File

@ -1,6 +1,6 @@
import { IconButton } from "@material/web/iconbutton/internal/icon-button.js"; import { IconButton } from "@material/web/iconbutton/internal/icon-button.js";
import { styles as sharedStyles } from "@material/web/iconbutton/internal/shared-styles.js"; import { styles as sharedStyles } from "@material/web/iconbutton/internal/shared-styles.css.js";
import { styles } from "@material/web/iconbutton/internal/standard-styles.js"; import { styles } from "@material/web/iconbutton/internal/standard-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,5 +1,5 @@
import { ListItemEl as ListItem } from "@material/web/list/internal/listitem/list-item.js"; import { ListItemEl as ListItem } from "@material/web/list/internal/listitem/list-item.js";
import { styles } from "@material/web/list/internal/listitem/list-item-styles.js"; import { styles } from "@material/web/list/internal/listitem/list-item-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,5 +1,5 @@
import { List } from "@material/web/list/internal/list.js"; import { List } from "@material/web/list/internal/list.js";
import { styles } from "@material/web/list/internal/list-styles.js"; import { styles } from "@material/web/list/internal/list-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,4 +1,4 @@
import { styles } from "@material/web/menu/internal/menuitem/menu-item-styles.js"; import { styles } from "@material/web/menu/internal/menuitem/menu-item-styles.css.js";
import { SelectOptionEl } from "@material/web/select/internal/selectoption/select-option.js"; import { SelectOptionEl } from "@material/web/select/internal/selectoption/select-option.js";
declare global { declare global {

View File

@ -1,6 +1,6 @@
import { styles as sharedStyles } from "@material/web/button/internal/shared-styles.js"; import { styles as sharedStyles } from "@material/web/button/internal/shared-styles.css.js";
import { TextButton } from "@material/web/button/internal/text-button.js"; import { TextButton } from "@material/web/button/internal/text-button.js";
import { styles as textStyles } from "@material/web/button/internal/text-styles.js"; import { styles as textStyles } from "@material/web/button/internal/text-styles.css.js";
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -1,7 +1,6 @@
import { ColoredConsole, coloredConsoleStyles } from "../util/console-color"; import { ColoredConsole, coloredConsoleStyles } from "../util/console-color";
import { sleep } from "../util/sleep"; import { sleep } from "../util/sleep";
import { LineBreakTransformer } from "../util/line-break-transformer"; import { LineBreakTransformer } from "../util/line-break-transformer";
import { TimestampTransformer } from "../util/timestamp-transformer";
import { Logger } from "../const"; import { Logger } from "../const";
export class EwtConsole extends HTMLElement { export class EwtConsole extends HTMLElement {
@ -96,7 +95,6 @@ export class EwtConsole extends HTMLElement {
signal: abortSignal, signal: abortSignal,
}) })
.pipeThrough(new TransformStream(new LineBreakTransformer())) .pipeThrough(new TransformStream(new LineBreakTransformer()))
.pipeThrough(new TransformStream(new TimestampTransformer()))
.pipeTo( .pipeTo(
new WritableStream({ new WritableStream({
write: (chunk) => { write: (chunk) => {

View File

@ -21,7 +21,7 @@ export const connect = async (button: InstallButton) => {
} }
try { try {
await port.open({ baudRate: 115200, bufferSize: 8192 }); await port.open({ baudRate: 115200 });
} catch (err: any) { } catch (err: any) {
alert(err.message); alert(err.message);
return; return;

View File

@ -6,7 +6,20 @@ import {
Manifest, Manifest,
FlashStateType, FlashStateType,
} from "./const"; } from "./const";
import { hardReset } from "./util/reset"; import { sleep } from "./util/sleep";
const resetTransport = async (transport: Transport) => {
await transport.device.setSignals({
dataTerminalReady: false,
requestToSend: true,
});
await sleep(250);
await transport.device.setSignals({
dataTerminalReady: false,
requestToSend: false,
});
await sleep(250);
};
export const flash = async ( export const flash = async (
onEvent: (state: FlashState) => void, onEvent: (state: FlashState) => void,
@ -54,14 +67,27 @@ export const flash = async (
"Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.", "Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
details: { error: FlashError.FAILED_INITIALIZING, details: err }, details: { error: FlashError.FAILED_INITIALIZING, details: err },
}); });
await resetTransport(transport);
await hardReset(transport);
await transport.disconnect(); await transport.disconnect();
return; return;
} }
chipFamily = esploader.chip.CHIP_NAME as any; chipFamily = esploader.chip.CHIP_NAME as any;
if (!esploader.chip.ROM_TEXT) {
fireStateEvent({
state: FlashStateType.ERROR,
message: `Chip ${chipFamily} is not supported`,
details: {
error: FlashError.NOT_SUPPORTED,
details: `Chip ${chipFamily} is not supported`,
},
});
await resetTransport(transport);
await transport.disconnect();
return;
}
fireStateEvent({ fireStateEvent({
state: FlashStateType.INITIALIZING, state: FlashStateType.INITIALIZING,
message: `Initialized. Found ${chipFamily}`, message: `Initialized. Found ${chipFamily}`,
@ -76,7 +102,7 @@ export const flash = async (
message: `Your ${chipFamily} board is not supported.`, message: `Your ${chipFamily} board is not supported.`,
details: { error: FlashError.NOT_SUPPORTED, details: chipFamily }, details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
}); });
await hardReset(transport); await resetTransport(transport);
await transport.disconnect(); await transport.disconnect();
return; return;
} }
@ -123,7 +149,7 @@ export const flash = async (
details: err.message, details: err.message,
}, },
}); });
await hardReset(transport); await resetTransport(transport);
await transport.disconnect(); await transport.disconnect();
return; return;
} }
@ -201,7 +227,7 @@ export const flash = async (
message: err.message, message: err.message,
details: { error: FlashError.WRITE_FAILED, details: err }, details: { error: FlashError.WRITE_FAILED, details: err },
}); });
await hardReset(transport); await resetTransport(transport);
await transport.disconnect(); await transport.disconnect();
return; return;
} }
@ -216,8 +242,9 @@ export const flash = async (
}, },
}); });
await hardReset(transport); await sleep(100);
console.log("HARD RESET");
await resetTransport(transport);
console.log("DISCONNECT"); console.log("DISCONNECT");
await transport.disconnect(); await transport.disconnect();

View File

@ -17,8 +17,6 @@ import "./pages/ewt-page-message";
import { import {
closeIcon, closeIcon,
listItemConsole, listItemConsole,
listItemEraseUserData,
listItemFundDevelopment,
listItemHomeAssistant, listItemHomeAssistant,
listItemInstallIcon, listItemInstallIcon,
listItemVisitDevice, listItemVisitDevice,
@ -42,7 +40,7 @@ import { version } from "./version";
import type { EwFilledSelect } from "./components/ew-filled-select"; import type { EwFilledSelect } from "./components/ew-filled-select";
console.log( console.log(
`ESP Web Tools ${version} by Open Home Foundation; https://esphome.github.io/esp-web-tools/`, `ESP Web Tools ${version} by Nabu Casa; https://esphome.github.io/esp-web-tools/`,
); );
const ERROR_ICON = "⚠️"; const ERROR_ICON = "⚠️";
@ -281,7 +279,6 @@ export class EwtInstallDialog extends LitElement {
href=${this._manifest.funding_url} href=${this._manifest.funding_url}
target="_blank" target="_blank"
> >
${listItemFundDevelopment}
<div slot="headline">Fund Development</div> <div slot="headline">Fund Development</div>
</ew-list-item> </ew-list-item>
` `
@ -293,7 +290,6 @@ export class EwtInstallDialog extends LitElement {
class="danger" class="danger"
@click=${() => this._startInstall(true)} @click=${() => this._startInstall(true)}
> >
${listItemEraseUserData}
<div slot="headline">Erase User Data</div> <div slot="headline">Erase User Data</div>
</ew-list-item> </ew-list-item>
` `
@ -923,13 +919,13 @@ export class EwtInstallDialog extends LitElement {
if (state.state === FlashStateType.FINISHED) { if (state.state === FlashStateType.FINISHED) {
sleep(100) sleep(100)
// Flashing closes the port // Flashing closes the port
.then(() => this.port.open({ baudRate: 115200, bufferSize: 8192 })) .then(() => this.port.open({ baudRate: 115200 }))
.then(() => this._initialize(true)) .then(() => this._initialize(true))
.then(() => this.requestUpdate()); .then(() => this.requestUpdate());
} else if (state.state === FlashStateType.ERROR) { } else if (state.state === FlashStateType.ERROR) {
sleep(100) sleep(100)
// Flashing closes the port // Flashing closes the port
.then(() => this.port.open({ baudRate: 115200, bufferSize: 8192 })); .then(() => this.port.open({ baudRate: 115200 }));
} }
}, },
this.port, this.port,

View File

@ -6,7 +6,6 @@ interface ConsoleState {
foregroundColor: string | null; foregroundColor: string | null;
backgroundColor: string | null; backgroundColor: string | null;
carriageReturn: boolean; carriageReturn: boolean;
lines: string[];
secret: boolean; secret: boolean;
} }
@ -19,7 +18,6 @@ export class ColoredConsole {
foregroundColor: null, foregroundColor: null,
backgroundColor: null, backgroundColor: null,
carriageReturn: false, carriageReturn: false,
lines: [],
secret: false, secret: false,
}; };
@ -29,13 +27,25 @@ export class ColoredConsole {
return this.targetElement.innerText; return this.targetElement.innerText;
} }
processLine(line: string): Element { addLine(line: string) {
// @ts-expect-error
const re = /(?:\033|\\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g; const re = /(?:\033|\\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g;
let i = 0; let i = 0;
if (this.state.carriageReturn) {
if (line !== "\n") {
// don't remove if \r\n
this.targetElement.removeChild(this.targetElement.lastChild!);
}
this.state.carriageReturn = false;
}
if (line.includes("\r")) {
this.state.carriageReturn = true;
}
const lineSpan = document.createElement("span"); const lineSpan = document.createElement("span");
lineSpan.classList.add("line"); lineSpan.classList.add("line");
this.targetElement.appendChild(lineSpan);
const addSpan = (content: string) => { const addSpan = (content: string) => {
if (content === "") return; if (content === "") return;
@ -168,52 +178,17 @@ export class ColoredConsole {
} }
} }
} }
addSpan(line.substring(i));
return lineSpan;
}
processLines() {
const atBottom = const atBottom =
this.targetElement.scrollTop > this.targetElement.scrollTop >
this.targetElement.scrollHeight - this.targetElement.offsetHeight - 50; this.targetElement.scrollHeight - this.targetElement.offsetHeight - 50;
const prevCarriageReturn = this.state.carriageReturn;
const fragment = document.createDocumentFragment();
if (this.state.lines.length == 0) { addSpan(line.substring(i));
return;
}
for (const line of this.state.lines) {
if (this.state.carriageReturn && line !== "\n") {
if (fragment.childElementCount) {
fragment.removeChild(fragment.lastChild!);
}
}
fragment.appendChild(this.processLine(line));
this.state.carriageReturn = line.includes("\r");
}
if (prevCarriageReturn && this.state.lines[0] !== "\n") {
this.targetElement.replaceChild(fragment, this.targetElement.lastChild!);
} else {
this.targetElement.appendChild(fragment);
}
this.state.lines = [];
// Keep scroll at bottom // Keep scroll at bottom
if (atBottom) { if (atBottom) {
this.targetElement.scrollTop = this.targetElement.scrollHeight; this.targetElement.scrollTop = this.targetElement.scrollHeight;
} }
} }
addLine(line: string) {
// Processing of lines is deferred for performance reasons
if (this.state.lines.length == 0) {
setTimeout(() => this.processLines(), 0);
}
this.state.lines.push(line);
}
} }
export const coloredConsoleStyles = ` export const coloredConsoleStyles = `

View File

@ -8,7 +8,7 @@ export class LineBreakTransformer implements Transformer<string, string> {
// Append new chunks to existing chunks. // Append new chunks to existing chunks.
this.chunks += chunk; this.chunks += chunk;
// For each line breaks in chunks, send the parsed lines out. // For each line breaks in chunks, send the parsed lines out.
const lines = this.chunks.split(/\r?\n/); const lines = this.chunks.split("\r\n");
this.chunks = lines.pop()!; this.chunks = lines.pop()!;
lines.forEach((line) => controller.enqueue(line + "\r\n")); lines.forEach((line) => controller.enqueue(line + "\r\n"));
} }

View File

@ -1,17 +0,0 @@
import { Transport } from "esptool-js";
import { sleep } from "./sleep";
export const hardReset = async (transport: Transport) => {
console.log("Triggering reset");
await transport.device.setSignals({
dataTerminalReady: false,
requestToSend: true,
});
await sleep(250);
await transport.device.setSignals({
dataTerminalReady: false,
requestToSend: false,
});
await sleep(250);
await new Promise((resolve) => setTimeout(resolve, 1000));
};

View File

@ -1,12 +0,0 @@
export class TimestampTransformer implements Transformer<string, string> {
transform(
chunk: string,
controller: TransformStreamDefaultController<string>,
) {
const date = new Date();
const h = date.getHours().toString().padStart(2, "0");
const m = date.getMinutes().toString().padStart(2, "0");
const s = date.getSeconds().toString().padStart(2, "0");
controller.enqueue(`[${h}:${m}:${s}]${chunk}`);
}
}

View File

@ -1,156 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="95.541344mm"
height="29.999447mm"
viewBox="0 0 95.54134 29.999447"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/erik/Documents/Projekt/NSPanel Manager/Logos/logo250.png"
inkscape:export-xdpi="76.010269"
inkscape:export-ydpi="76.010269"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="false"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.3404872"
inkscape:cx="174.5634"
inkscape:cy="71.988753"
inkscape:window-width="1366"
inkscape:window-height="704"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer2"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="5"
fit-margin-left="7"
fit-margin-right="7"
fit-margin-bottom="5" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(-23.806324,-37.155967)">
<rect
style="fill:#000000;stroke:#000000;stroke-width:0.264583"
id="rect91625"
width="16.863066"
height="1.0996726"
x="30.938616"
y="59.758064" />
<rect
style="fill:#000000;stroke:#000000;stroke-width:0.473039"
id="rect91761"
width="66.510048"
height="0.89121634"
x="31.042845"
y="42.392487" />
<rect
style="fill:#000000;stroke:#000000;stroke-width:0.34752"
id="rect91763"
width="31.46484"
height="1.0167363"
x="80.709068"
y="59.79953" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
style="display:inline"
transform="translate(-23.806324,-37.155967)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:0;font-family:sans-serif;letter-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="64.189873"
y="56.76144"
id="text39249"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.4056px;line-height:0.55;font-family:Hamlin;-inkscape-font-specification:'Hamlin, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="64.189873"
y="56.76144"
id="tspan62001">NSPANEL</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:1.55;font-family:Hamlin;-inkscape-font-specification:'Hamlin Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;stroke-width:0.264583"
x="64.189873"
y="62.091209"
id="tspan73527">MANAGER</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 3"
transform="translate(-23.806324,-37.155967)">
<g
id="g91894"
transform="matrix(0.15910033,0,0,0.15910033,102.25849,44.830316)">
<g
inkscape:label="Layer 1"
id="layer1-1"
transform="translate(-30.269979,-9.7200098)"
style="display:inline;fill:#ffc101;fill-opacity:1">
<path
style="fill:#ffc101;fill-opacity:1;stroke-width:0.103892"
d="m 55.789326,41.355207 -7.50621,-0.0036 c -8.143469,-0.0039 -15.508914,-0.135689 -16.902471,-0.302478 -0.482408,-0.05774 -0.940146,-0.168019 -1.017198,-0.245069 -0.115522,-0.115523 -0.121188,-0.512246 -0.03231,-2.261925 0.23677,-4.661121 1.011239,-7.815267 2.954845,-12.034057 2.163281,-4.695621 5.19604,-8.340625 9.101253,-10.938592 2.710535,-1.8032 6.982591,-3.536303 9.582241,-3.887359 0.270422,-0.03652 0.543941,-0.129371 0.60782,-0.20634 0.06388,-0.07697 0.286764,-0.168201 0.495299,-0.202735 0.208537,-0.03453 0.449284,-0.09017 0.534995,-0.123628 0.168484,-0.06577 0.381034,-0.08553 1.215776,-0.112981 l 0.540477,-0.01778 0.03093,-0.649326 0.03093,-0.6493272 h 1.610329 1.610329 l 0.03093,0.6493272 0.03093,0.649326 0.540477,0.01778 c 0.834742,0.02745 1.047292,0.04721 1.215776,0.112981 0.08571,0.03346 0.326458,0.08909 0.534995,0.123628 0.208535,0.03453 0.43142,0.125765 0.495299,0.202735 0.06388,0.07697 0.337398,0.169823 0.60782,0.20634 1.199505,0.161981 3.423686,0.820696 4.997483,1.480058 3.507719,1.469604 6.221358,3.303143 8.667596,5.856479 2.140092,2.233788 3.602852,4.416791 5.018415,7.489412 1.943606,4.21879 2.718075,7.372936 2.954845,12.034057 0.08888,1.749679 0.08321,2.146402 -0.03231,2.261925 -0.07705,0.07705 -0.53479,0.187332 -1.017198,0.245069 -1.393557,0.166789 -8.759002,0.298597 -16.902471,0.302478 l -7.50621,0.0036 z"
id="path1465"
inkscape:connector-curvature="0"
inkscape:export-xdpi="15.183412"
inkscape:export-ydpi="15.183412"
sodipodi:nodetypes="csssssssssscccccccccscssssssssccc" />
</g>
<g
inkscape:label="Layer 1 copy"
id="g2878"
transform="translate(-30.269979,-9.7200098)"
style="display:inline">
<path
style="fill:#000000;fill-opacity:1;stroke-width:0.103892"
d="m 54.334834,101.13608 c -3.956539,-0.21732 -5.98252,-0.58093 -6.463066,-1.159955 -0.09761,-0.11762 -0.123549,-0.57148 -0.09916,-1.73541 0.02799,-1.33577 0.008,-1.59689 -0.130816,-1.71212 -0.09011,-0.0748 -0.16384,-0.29294 -0.16384,-0.48477 0,-0.30436 0.05486,-0.37678 0.430667,-0.5685 0.694102,-0.3541 2.301256,-0.71174 4.060659,-0.9036 l 1.638312,-0.17866 V 70.783119 c 0,-15.670705 -0.03494,-23.609951 -0.103892,-23.609951 -0.05714,0 -0.103892,-0.0935 -0.103892,-0.207784 0,-0.194592 0.06926,-0.207784 1.090868,-0.207784 0.900399,0 1.090868,-0.02721 1.090868,-0.155839 0,-0.08571 0.04675,-0.155838 0.103892,-0.155838 0.06644,0 0.103892,-3.463072 0.103892,-5.090716 l 2.493412,-2e-6 c 0,1.627644 0.03746,5.090716 0.103892,5.090716 0.05714,0 0.103892,0.07013 0.103892,0.155838 0,0.128629 0.190469,0.155839 1.090868,0.155839 1.021606,0 1.090868,0.01319 1.090868,0.207784 0,0.114281 -0.04675,0.207784 -0.103892,0.207784 -0.06896,0 -0.103892,7.939246 -0.103892,23.609951 v 23.609948 l 1.638312,0.17866 c 1.759403,0.19186 3.366557,0.54949 4.060659,0.9036 0.375812,0.19172 0.430667,0.26413 0.430667,0.5685 0,0.19183 -0.07373,0.40998 -0.16384,0.48476 -0.138668,0.11509 -0.158971,0.37643 -0.132144,1.70097 0.01743,0.86075 -0.0055,1.62411 -0.05099,1.69635 -0.27864,0.442605 -1.632284,0.804945 -3.803075,1.017985 -1.460176,0.14331 -6.726732,0.26812 -8.109225,0.19219 z"
id="path2876"
inkscape:connector-curvature="0"
inkscape:export-xdpi="15.183412"
inkscape:export-ydpi="15.183412"
sodipodi:nodetypes="ssssssscssssssccsssssscssssscsss" />
</g>
<g
id="layer3-5"
inkscape:label="Layer 3">
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:17.1351;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
id="path1510"
cx="34.837666"
cy="55.072739"
r="1.2872558" />
</g>
<g
id="layer2-9"
inkscape:label="Layer 2">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:12.8043;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
id="rect1508"
width="1.3904994"
height="22.681686"
x="34.147842"
y="31.601988" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB