diff --git a/README.md b/README.md
index d63778d..21997c3 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+

+
# PIGWay Frame Flow
Development currently supports Python 3.14.x. See [contributing](CONTRIBUTING.md) and [security reporting](SECURITY.md).
diff --git a/README.zh-CN.md b/README.zh-CN.md
index cd40bf1..299f55c 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,3 +1,5 @@
+
+
# PIGWay Frame Flow
开发环境目前支持 Python 3.14.x。参见[贡献指南](CONTRIBUTING.md)和[安全问题报告](SECURITY.md)。
diff --git a/SHA256SUMS b/SHA256SUMS
index d834d88..46706ff 100644
--- a/SHA256SUMS
+++ b/SHA256SUMS
@@ -8,8 +8,8 @@ c8c545718e9b9880555c950a078cac160c82e0cd6e593afcf20bc13727539ec5 .github/workfl
0acd6eb62044b52828dae0dbd6f34ae91fb64ea1b6fa9f67062c6125a7364e40 Dockerfile
6eb7728e6e73f9568ca6fd5ad6383d13ffc638f2870fefa2d86f72f39685c0fc LICENSE
250a2bcec248b074786d90e0329874628d70d5a0fe3b4fba47c8c57d4f84dcce NOTICE.md
-26ad0d9063c0ee843f0f532604b8fd66e8b39fd72d129595ed078677c0d6d13c README.md
-ee00cb267b38485f1e574d610f0a57b51d4ae575731a18e57d06a7c43dc0a8b0 README.zh-CN.md
+b7a41a2c00df1a0dbe3869d059d65c3bcaee8c50e51217c358527c4f1d3d6c98 README.md
+a4dc99f04b3f6527559cc6adbe8fe789e2795e28bfa976d4243fc0c23900b5d3 README.zh-CN.md
4eb58ccd6f4b073d6f6e2ed2ca1ce430813064831b1143d63c6ab8b0c453638f RELEASE_NOTES.md
3dd364e47fdbd2ce9ce2df83342c523adc21583f093a5439d501b3424878ce2f SECURITY.md
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 build-cache/wheels/.gitkeep
@@ -32,6 +32,10 @@ b48f3ab6f28404bb34c188067a832384f4ba290e56912461ef3565528aa157d8 docs/PUBLICATI
5eb19e7e4ed1ceddbdf388fd55697e40f1b7ecd9a4fbdf8ab485132cc684dcb2 docs/REAL_DEVICE_TEST.md
bfc38b889fa966031e195c3131db1d4a713e0dc35c644e3047c7ed94f2354700 docs/RESEARCH.md
fac36f986e1a7a331d3669aa8e2013e6c0ea7a99c16a61ba3ff573e836aa87e1 docs/SYNOLOGY_PHOTOS_API.md
+f8ef7749c58d4396781bbc21371048f6561e791efbd489570224dc51173bffff docs/brand/README.md
+f709156217c798f6b6230dae1f341373e0814a67ab90e881401da577f52ec3d3 docs/brand/pigway-avatar.png
+da6734d8a23305f6254c0936dadff4ec34977b4c934f5067186423c8cfce3fc7 docs/brand/pigway-logo.png
+0fa494a3fbd456c754bbea3f7f050be139e32e3b42b09a1ed953094087437210 docs/brand/pigway-logo.svg
ac0481196e593ff9cca2e5133ff41fb3614268e6a10728a0deca3ad3bfb80b34 docs/integrations/TICKTAR.md
1794e3b09fa4eb9fb59cd18f729f49d6387885fc97a0b4f227e825cc49603554 docs/openapi.json
54bf7061e0626900ce35e19e81efc5c32c8fd6a0438bdad68f494e1ce81616d4 docs/research/KOKONNA_ACCOUNT_CAPABILITIES.md
@@ -40,7 +44,7 @@ ec53d587b855be84283c50f8fb45be62a4c1caba78b28d436a8260fd171943a2 docs/research/
2a68fe95ba4afc24aec6abd3e6ab24a360eb6bc3005ab0fefd696190915d76c1 docs/research/KOKONNA_WIDGET_CATALOG.json
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 pigway_frame_flow/__init__.py
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 pigway_frame_flow/api/__init__.py
-53aa03c15f847a1d770f2132d117f793602ecf766b6ab92973bcdf2169946eae pigway_frame_flow/api/app.py
+421400bf250f818abe90e9d503944666814dc47622743b24456ee3136d67bd53 pigway_frame_flow/api/app.py
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 pigway_frame_flow/core/__init__.py
fbadf6beaf25b9670dcbf125ec9d192188cf9e076ee4d4a0b5decdaf3834417c pigway_frame_flow/core/accounts.py
818e8fb29d22c13159a6cb22bcbc6b9e211a8a1301588126eb192ac4ed71441b pigway_frame_flow/core/auth.py
@@ -117,11 +121,15 @@ f9e777980ea5bb3ea8fb2357f23530802811ea2674d25100af9b728cbc9f83a9 pigway_frame_f
82c17f4e7eaac95483592add181f549186dea9d7c0375fa9cad3554e3ccd1794 pigway_frame_flow/templates/system-alert.json
d6f705b9d183b57d50759e283db2161f8493c4e3969459e74da635252014c925 pigway_frame_flow/templates/weather.json
3acad7c209e3483481c3b966b27a0609f2d12c2383955274dfd226a1fe5f41bf pigway_frame_flow/web/app.js
+f332fac63f611572597812a06ae0cc266a59e5677efda0d6e9b6925acf9f9309 pigway_frame_flow/web/apple-touch-icon.png
d7d55b06ead173b4c23a277fa39800cadfc18f1163c500e8617cfc1f4ee0dea9 pigway_frame_flow/web/extensions.js
+94558dbf524a1068e83283eb814c1b1501512d4b7e75e092b52e9aabfea720e7 pigway_frame_flow/web/favicon.ico
+0fa494a3fbd456c754bbea3f7f050be139e32e3b42b09a1ed953094087437210 pigway_frame_flow/web/favicon.svg
cd3dc54289418eea667ad71e236cce61e9dc6add4925bdb46c8a9b7c19860c48 pigway_frame_flow/web/features.js
a43bde8abb2ac9a9e0f41f83219c43c20804d76f0984407308847238554b1f3d pigway_frame_flow/web/i18n.js
-d2eae1d2116dc426da00b709154516ca633ee0cd41abd23a5d8f165b1f1673ef pigway_frame_flow/web/index.html
-5c19817302af4e6c9b10cb8b9bb18cf139967d808bcca7660fb2d37a7a16608c pigway_frame_flow/web/style.css
+496efa44ae307a25b1aec0d5b4c4f20461767158b7ff98c6abe83bf88021b278 pigway_frame_flow/web/index.html
+0fa494a3fbd456c754bbea3f7f050be139e32e3b42b09a1ed953094087437210 pigway_frame_flow/web/logo-symbol.svg
+246256009927ec8a92d6c9a96eb0b1cecc741c48544c1d7080d12b6ee3255b49 pigway_frame_flow/web/style.css
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 pigway_frame_flow/web/swagger-LICENSE
050bc415ee7048dcd881682678f720264e7da5e373f7461d7c58c755305255f7 pigway_frame_flow/web/swagger-ui-bundle.js
1ac324f7dcd27e4b9386b4bd6421271ec147e922a22c05ba24b11515e9aa6321 pigway_frame_flow/web/swagger-ui.css
@@ -173,7 +181,7 @@ d4f04e4c8f51c56aa21389d60115ecd1dda384da444dfd4d40237314fb51e3ea tests/conftest
c2c398d7323e47255646e580c498a211a8f1a4fee32861954eaa29628bec8301 tests/schema-contract.json
14a7c56c40ea4b520585c20341b3ec70fa3ba52518144a10ba3494ceceb30758 tests/test_accounts.py
e87ce325c8260f96e586b20697ab0bac71058918ba73f7412eb482364f83f73e tests/test_ai_images.py
-94c01e6d8bdbfffbff72edb91823eaf39a325ad8782aba1d8e2804402e256aec tests/test_api.py
+a6edb781f4d081b2db45ffe2b02eed52a10f9b87a70e1e201b5d2f7c6813b93b tests/test_api.py
24fca799b0f5e33c800c6310764b18f6b39fc210be769684171388cf14b95746 tests/test_audit_regressions.py
7001a2209473e6f6bf1050d33212985f63f4844defeb5df8e75d9f2a96c0d83c tests/test_auto_collage.py
f267ebcdb3cf1fcb01ef9fd9ef7c5c8c4daca11d02543f84bfbe56359f35b78a tests/test_ci_audit.py
diff --git a/docs/brand/README.md b/docs/brand/README.md
new file mode 100644
index 0000000..0776679
--- /dev/null
+++ b/docs/brand/README.md
@@ -0,0 +1,15 @@
+# PIGWAY 品牌资源 / Brand assets
+
+来源:PIGWAY Brand Production v1.1 正式交付包。图形、颜色与比例保持原文件,不使用重绘、滤镜或替代字标。
+
+- `pigway-logo.svg`:正式 Web 图形标。
+- `pigway-logo.png`:正式 256px 透明图形标,用于项目介绍页。
+- `pigway-avatar.png`:正式 512px 平台图标,用于 Tea 项目头像。
+
+本项目的产品名称保持独立。品牌物料版本不跟随软件版本变化;网页小图标使用同一正式包的 Favicon 和 Apple Touch Icon。
+
+## SHA-256
+
+- `0fa494a3fbd456c754bbea3f7f050be139e32e3b42b09a1ed953094087437210` `pigway-logo.svg`
+- `da6734d8a23305f6254c0936dadff4ec34977b4c934f5067186423c8cfce3fc7` `pigway-logo.png`
+- `f709156217c798f6b6230dae1f341373e0814a67ab90e881401da577f52ec3d3` `pigway-avatar.png`
diff --git a/docs/brand/pigway-avatar.png b/docs/brand/pigway-avatar.png
new file mode 100644
index 0000000..7a525bf
Binary files /dev/null and b/docs/brand/pigway-avatar.png differ
diff --git a/docs/brand/pigway-logo.png b/docs/brand/pigway-logo.png
new file mode 100644
index 0000000..2fb3ad3
Binary files /dev/null and b/docs/brand/pigway-logo.png differ
diff --git a/docs/brand/pigway-logo.svg b/docs/brand/pigway-logo.svg
new file mode 100644
index 0000000..744b82a
--- /dev/null
+++ b/docs/brand/pigway-logo.svg
@@ -0,0 +1 @@
+
diff --git a/pigway_frame_flow/api/app.py b/pigway_frame_flow/api/app.py
index 27051ce..447acf3 100644
--- a/pigway_frame_flow/api/app.py
+++ b/pigway_frame_flow/api/app.py
@@ -1313,6 +1313,10 @@ def create_app(settings=None, start_worker=True):
"i18n.js",
"swagger-ui-bundle.js",
"swagger-ui.css",
+ "favicon.svg",
+ "favicon.ico",
+ "apple-touch-icon.png",
+ "logo-symbol.svg",
]:
raise FlowError(E.NOT_FOUND, "Resource not found", 404)
return FileResponse(root / filename)
diff --git a/pigway_frame_flow/web/apple-touch-icon.png b/pigway_frame_flow/web/apple-touch-icon.png
new file mode 100644
index 0000000..c2b09a2
Binary files /dev/null and b/pigway_frame_flow/web/apple-touch-icon.png differ
diff --git a/pigway_frame_flow/web/favicon.ico b/pigway_frame_flow/web/favicon.ico
new file mode 100644
index 0000000..424320f
Binary files /dev/null and b/pigway_frame_flow/web/favicon.ico differ
diff --git a/pigway_frame_flow/web/favicon.svg b/pigway_frame_flow/web/favicon.svg
new file mode 100644
index 0000000..744b82a
--- /dev/null
+++ b/pigway_frame_flow/web/favicon.svg
@@ -0,0 +1 @@
+
diff --git a/pigway_frame_flow/web/index.html b/pigway_frame_flow/web/index.html
index 1e4ffb2..beccd73 100644
--- a/pigway_frame_flow/web/index.html
+++ b/pigway_frame_flow/web/index.html
@@ -1,3 +1,3 @@
-PIGWay Frame Flow
-
+PIGWay Frame Flow
+
PIGWay Frame Flow
diff --git a/pigway_frame_flow/web/logo-symbol.svg b/pigway_frame_flow/web/logo-symbol.svg
new file mode 100644
index 0000000..744b82a
--- /dev/null
+++ b/pigway_frame_flow/web/logo-symbol.svg
@@ -0,0 +1 @@
+
diff --git a/pigway_frame_flow/web/style.css b/pigway_frame_flow/web/style.css
index 046b725..73f6115 100644
--- a/pigway_frame_flow/web/style.css
+++ b/pigway_frame_flow/web/style.css
@@ -227,3 +227,7 @@ form#mqtt-config{grid-template-columns:minmax(160px,1fr) 110px minmax(180px,1.5f
.history-table td{vertical-align:middle}
.image-card h3.source-path{white-space:normal;overflow-wrap:anywhere}
+
+.pigway-logo{display:block;width:56px;height:56px;padding:6px;flex:0 0 56px;object-fit:contain}
+.brand>span{min-width:0}.workspace-heading{display:flex;align-items:center;gap:12px}.workspace-heading>div{min-width:0}.mobile-brand-logo{display:none}
+@media(max-width:720px){.mobile-brand-logo{display:block}}
diff --git a/tests/test_api.py b/tests/test_api.py
index 288ec87..aba912b 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -291,6 +291,28 @@ def test_core_has_no_client_special_case():
)
+def test_official_brand_assets(client):
+ html = client.get("/").text
+ assert "/ui/favicon.svg" in html
+ assert "/ui/apple-touch-icon.png" in html
+ assert 'class="mobile-brand-logo pigway-logo"' in html
+ for filename, content_type in [
+ ("logo-symbol.svg", "image/svg+xml"),
+ ("favicon.svg", "image/svg+xml"),
+ ("favicon.ico", "image/vnd.microsoft.icon"),
+ ("apple-touch-icon.png", "image/png"),
+ ]:
+ response = client.get("/ui/" + filename)
+ assert response.status_code == 200
+ assert response.headers["content-type"].split(";")[0] in (
+ content_type,
+ "image/x-icon",
+ )
+ assert (
+ response.content == (Path("pigway_frame_flow/web") / filename).read_bytes()
+ )
+
+
def test_ui_languages(client):
assert client.get("/ui/app.js").status_code == 200
i18n = client.get("/ui/i18n.js").text