
























@@ -80,6 +80,17 @@ final class DashboardWindowController: NSWindowController, WKNavigationDelegate,
8080self.window?.performClose(nil)
8181}
828283+func showFailure(title: String, message: String, detail: String? = nil) {
84+self.currentURL = URL(string: "about:blank")!
85+self.auth = DashboardWindowAuth(gatewayUrl: nil, token: nil, password: nil)
86+self.refreshNativeAuthScript(url: self.currentURL, auth: self.auth)
87+self.webView.stopLoading()
88+self.webView.loadHTMLString(
89+Self.failureHTML(title: title, message: message, detail: detail, url: nil),
90+ baseURL: nil)
91+self.show()
92+}
93+8394private func load(_ url: URL) {
8495 dashboardWindowLogger.debug("dashboard load \(url.absoluteString, privacy: .public)")
8596self.webView.load(URLRequest(url: url))
@@ -282,54 +293,107 @@ final class DashboardWindowController: NSWindowController, WKNavigationDelegate,
282293if nsError.domain == NSURLErrorDomain, nsError.code == NSURLErrorCancelled { return }
283294 dashboardWindowLogger.error(
284295"dashboard load failed url=\(self.currentURL.absoluteString, privacy: .public) error=\(error.localizedDescription, privacy: .public)")
285-let html = Self.failureHTML(url: self.currentURL, message: error.localizedDescription)
296+let html = Self.failureHTML(
297+ title: "Dashboard unavailable",
298+ message: error.localizedDescription,
299+ detail: "The dashboard window is open, but the web UI could not load from this endpoint.",
300+ url: self.currentURL)
286301self.webView.loadHTMLString(html, baseURL: nil)
287302}
288303289-private static func failureHTML(url: URL, message: String) -> String {
290-"""
304+private static func failureHTML(title: String, message: String, detail: String?, url: URL?) -> String {
305+let detailHTML = detail.map { "<p class=\"detail\">\(self.htmlEscape($0))</p>" } ?? ""
306+let urlHTML = url.map { "<code>\(self.htmlEscape($0.absoluteString))</code>" } ?? ""
307+return """
291308 <!doctype html>
292309 <html>
293310 <head>
294311 <meta charset="utf-8">
295312 <style>
296313 :root { color-scheme: light dark; }
314+ * { box-sizing: border-box; }
297315 body {
298316 margin: 0;
299317 min-height: 100vh;
300318 display: grid;
301319 place-items: center;
302- background: Canvas;
303- color: CanvasText;
304- font: -apple-system-body;
320+ background: #101114;
321+ color: rgba(255,255,255,.92);
322+ font: 15px -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
305323 }
306324 main {
307- width: min(520px, calc(100vw - 64px));
308- line-height: 1.4;
325+ width: min(540px, calc(100vw - 72px));
326+ padding: 34px;
327+ border: 1px solid rgba(255,255,255,.12);
328+ border-radius: 22px;
329+ background: rgba(255,255,255,.035);
330+ box-shadow: 0 28px 90px rgba(0,0,0,.36);
331+ line-height: 1.45;
332+ }
333+ .badge {
334+ width: 44px;
335+ height: 44px;
336+ display: grid;
337+ place-items: center;
338+ margin-bottom: 20px;
339+ border-radius: 14px;
340+ background: rgba(255,255,255,.07);
341+ color: #ff746b;
342+ font-size: 24px;
309343 }
310344 h1 {
311- margin: 0 0 10px;
312- font: -apple-system-title2;
313- font-weight: 650;
345+ margin: 0 0 12px;
346+ font-size: 24px;
347+ line-height: 1.16;
348+ font-weight: 700;
349+ letter-spacing: 0;
350+ }
351+ p {
352+ margin: 0;
353+ color: rgba(255,255,255,.76);
354+ font-size: 16px;
355+ }
356+ .detail {
357+ margin-top: 14px;
358+ color: rgba(255,255,255,.56);
359+ font-size: 13px;
314360 }
315- p { margin: 8px 0; color: color-mix(in srgb, CanvasText 72%, transparent); }
316361 code {
317362 display: block;
318- margin-top: 14px;
363+ margin-top: 18px;
319364 padding: 12px;
320- border-radius: 8px;
321- background: color-mix(in srgb, CanvasText 8%, transparent);
322- color: CanvasText;
365+ border: 1px solid rgba(255,255,255,.08);
366+ border-radius: 10px;
367+ background: rgba(0,0,0,.26);
368+ color: rgba(255,255,255,.76);
323369 overflow-wrap: anywhere;
324370 font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
325371 }
372+ @media (prefers-color-scheme: light) {
373+ body { background: #f5f6f8; color: rgba(0,0,0,.86); }
374+ main {
375+ background: rgba(255,255,255,.84);
376+ border-color: rgba(0,0,0,.1);
377+ box-shadow: 0 28px 90px rgba(0,0,0,.12);
378+ }
379+ .badge { background: rgba(0,0,0,.06); }
380+ p { color: rgba(0,0,0,.68); }
381+ .detail { color: rgba(0,0,0,.54); }
382+ code {
383+ background: rgba(0,0,0,.05);
384+ border-color: rgba(0,0,0,.08);
385+ color: rgba(0,0,0,.68);
386+ }
387+ }
326388 </style>
327389 </head>
328390 <body>
329391 <main>
330- <h1>Dashboard unavailable</h1>
392+ <div class="badge">!</div>
393+ <h1>\(self.htmlEscape(title))</h1>
331394 <p>\(self.htmlEscape(message))</p>
332- <code>\(self.htmlEscape(url.absoluteString))</code>
395+ \(detailHTML)
396+ \(urlHTML)
333397 </main>
334398 </body>
335399 </html>
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。