@ogygia/devtools 0.0.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +24 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +24 -0
- package/dist/devtools/BoundaryOverlay.svelte +396 -0
- package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
- package/dist/devtools/Devtools.svelte +663 -0
- package/dist/devtools/Devtools.svelte.d.ts +15 -0
- package/dist/devtools/HubTab.svelte +206 -0
- package/dist/devtools/HubTab.svelte.d.ts +11 -0
- package/dist/devtools/HydrationTab.svelte +237 -0
- package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
- package/dist/devtools/IslandDetail.svelte +615 -0
- package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
- package/dist/devtools/LedgerTab.svelte +287 -0
- package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
- package/dist/devtools/LensTab.svelte +290 -0
- package/dist/devtools/LensTab.svelte.d.ts +19 -0
- package/dist/devtools/NavTab.svelte +229 -0
- package/dist/devtools/NavTab.svelte.d.ts +11 -0
- package/dist/devtools/PageTab.svelte +1122 -0
- package/dist/devtools/PageTab.svelte.d.ts +15 -0
- package/dist/devtools/ProfilerTab.svelte +755 -0
- package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
- package/dist/devtools/PropsTree.svelte +155 -0
- package/dist/devtools/PropsTree.svelte.d.ts +15 -0
- package/dist/devtools/SessionTab.svelte +796 -0
- package/dist/devtools/SessionTab.svelte.d.ts +11 -0
- package/dist/devtools/TimelineTab.svelte +267 -0
- package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
- package/dist/devtools/WireTab.svelte +149 -0
- package/dist/devtools/WireTab.svelte.d.ts +11 -0
- package/dist/devtools/bytes.d.ts +70 -0
- package/dist/devtools/bytes.js +82 -0
- package/dist/devtools/cache-headers.d.ts +20 -0
- package/dist/devtools/cache-headers.js +81 -0
- package/dist/devtools/highlight.d.ts +7 -0
- package/dist/devtools/highlight.js +83 -0
- package/dist/devtools/hydration.d.ts +48 -0
- package/dist/devtools/hydration.js +93 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +3 -0
- package/dist/devtools/ledger-dom.d.ts +7 -0
- package/dist/devtools/ledger-dom.js +151 -0
- package/dist/devtools/leftovers.d.ts +77 -0
- package/dist/devtools/leftovers.js +319 -0
- package/dist/devtools/names.d.ts +1 -0
- package/dist/devtools/names.js +34 -0
- package/dist/devtools/page.d.ts +96 -0
- package/dist/devtools/page.js +957 -0
- package/dist/devtools/profile-anchors.d.ts +22 -0
- package/dist/devtools/profile-anchors.js +135 -0
- package/dist/devtools/profile-store.d.ts +175 -0
- package/dist/devtools/profile-store.js +239 -0
- package/dist/devtools/regions.d.ts +77 -0
- package/dist/devtools/regions.js +132 -0
- package/dist/devtools/session.d.ts +31 -0
- package/dist/devtools/session.js +455 -0
- package/dist/devtools/since-load.d.ts +50 -0
- package/dist/devtools/since-load.js +89 -0
- package/dist/devtools/source-lines.d.ts +24 -0
- package/dist/devtools/source-lines.js +106 -0
- package/dist/devtools/styles.d.ts +52 -0
- package/dist/devtools/styles.js +248 -0
- package/dist/devtools/testing-engine.d.ts +83 -0
- package/dist/devtools/testing-engine.js +220 -0
- package/dist/devtools/ui-app.d.ts +5 -0
- package/dist/devtools/ui-app.js +69 -0
- package/dist/devtools/ui.d.ts +50 -0
- package/dist/devtools/ui.js +175 -0
- package/dist/profiler/alloc.d.ts +75 -0
- package/dist/profiler/alloc.js +76 -0
- package/dist/profiler/analyze.d.ts +437 -0
- package/dist/profiler/analyze.js +1941 -0
- package/dist/profiler/app-path.d.ts +7 -0
- package/dist/profiler/app-path.js +26 -0
- package/dist/profiler/async-io.d.ts +58 -0
- package/dist/profiler/async-io.js +187 -0
- package/dist/profiler/beacon-standalone.d.ts +4 -0
- package/dist/profiler/beacon-standalone.js +409 -0
- package/dist/profiler/browser-findings.d.ts +25 -0
- package/dist/profiler/browser-findings.js +260 -0
- package/dist/profiler/byte-strip.d.ts +58 -0
- package/dist/profiler/byte-strip.js +228 -0
- package/dist/profiler/client-files.d.ts +16 -0
- package/dist/profiler/client-files.js +60 -0
- package/dist/profiler/client-windows.d.ts +49 -0
- package/dist/profiler/client-windows.js +95 -0
- package/dist/profiler/client.d.ts +40 -0
- package/dist/profiler/client.js +82 -0
- package/dist/profiler/compare.d.ts +308 -0
- package/dist/profiler/compare.js +733 -0
- package/dist/profiler/compress.d.ts +10 -0
- package/dist/profiler/compress.js +36 -0
- package/dist/profiler/contention.d.ts +60 -0
- package/dist/profiler/contention.js +86 -0
- package/dist/profiler/crypto.d.ts +19 -0
- package/dist/profiler/crypto.js +101 -0
- package/dist/profiler/doc-diff.d.ts +47 -0
- package/dist/profiler/doc-diff.js +214 -0
- package/dist/profiler/dom-diff.d.ts +43 -0
- package/dist/profiler/dom-diff.js +121 -0
- package/dist/profiler/drill.d.ts +89 -0
- package/dist/profiler/drill.js +553 -0
- package/dist/profiler/embedded-maps.d.ts +37 -0
- package/dist/profiler/embedded-maps.js +135 -0
- package/dist/profiler/env.d.ts +6 -0
- package/dist/profiler/env.js +10 -0
- package/dist/profiler/forecast.d.ts +76 -0
- package/dist/profiler/forecast.js +225 -0
- package/dist/profiler/frames.d.ts +11 -0
- package/dist/profiler/frames.js +42 -0
- package/dist/profiler/gc.d.ts +144 -0
- package/dist/profiler/gc.js +271 -0
- package/dist/profiler/hole-slots.d.ts +31 -0
- package/dist/profiler/hole-slots.js +90 -0
- package/dist/profiler/index.d.ts +288 -0
- package/dist/profiler/index.js +4921 -0
- package/dist/profiler/insights.d.ts +96 -0
- package/dist/profiler/insights.js +239 -0
- package/dist/profiler/ledger.d.ts +121 -0
- package/dist/profiler/ledger.js +440 -0
- package/dist/profiler/lineage.d.ts +81 -0
- package/dist/profiler/lineage.js +359 -0
- package/dist/profiler/module-map.d.ts +12 -0
- package/dist/profiler/module-map.js +94 -0
- package/dist/profiler/net.d.ts +169 -0
- package/dist/profiler/net.js +586 -0
- package/dist/profiler/on-demand.d.ts +11 -0
- package/dist/profiler/on-demand.js +15 -0
- package/dist/profiler/own-requests.d.ts +9 -0
- package/dist/profiler/own-requests.js +17 -0
- package/dist/profiler/patterns.d.ts +476 -0
- package/dist/profiler/patterns.js +2203 -0
- package/dist/profiler/profiler-router.d.ts +245 -0
- package/dist/profiler/profiler-router.js +113 -0
- package/dist/profiler/refresh-stub.d.ts +2 -0
- package/dist/profiler/refresh-stub.js +37 -0
- package/dist/profiler/report.d.ts +1404 -0
- package/dist/profiler/report.js +2638 -0
- package/dist/profiler/river.d.ts +65 -0
- package/dist/profiler/river.js +298 -0
- package/dist/profiler/route-files.d.ts +22 -0
- package/dist/profiler/route-files.js +55 -0
- package/dist/profiler/route-imports.d.ts +14 -0
- package/dist/profiler/route-imports.js +83 -0
- package/dist/profiler/saving.d.ts +22 -0
- package/dist/profiler/saving.js +42 -0
- package/dist/profiler/score.d.ts +109 -0
- package/dist/profiler/score.js +261 -0
- package/dist/profiler/session-cookie.d.ts +28 -0
- package/dist/profiler/session-cookie.js +45 -0
- package/dist/profiler/sink.d.ts +81 -0
- package/dist/profiler/sink.js +124 -0
- package/dist/profiler/site-fixes.d.ts +25 -0
- package/dist/profiler/site-fixes.js +62 -0
- package/dist/profiler/source-scan.d.ts +210 -0
- package/dist/profiler/source-scan.js +1724 -0
- package/dist/profiler/span.d.ts +90 -0
- package/dist/profiler/span.js +109 -0
- package/dist/profiler/stacks.d.ts +43 -0
- package/dist/profiler/stacks.js +107 -0
- package/dist/profiler/standalone.d.ts +27 -0
- package/dist/profiler/standalone.js +98 -0
- package/dist/profiler/steps.d.ts +39 -0
- package/dist/profiler/steps.js +74 -0
- package/dist/profiler/storage/index.d.ts +87 -0
- package/dist/profiler/storage/index.js +76 -0
- package/dist/profiler/storage/postgres.d.ts +12 -0
- package/dist/profiler/storage/postgres.js +94 -0
- package/dist/profiler/storage/redis.d.ts +19 -0
- package/dist/profiler/storage/redis.js +148 -0
- package/dist/profiler/storage/sqlite.d.ts +2 -0
- package/dist/profiler/storage/sqlite.js +85 -0
- package/dist/profiler/storage/visits.d.ts +4 -0
- package/dist/profiler/storage/visits.js +6 -0
- package/dist/profiler/stream-tail.d.ts +26 -0
- package/dist/profiler/stream-tail.js +74 -0
- package/dist/profiler/timeline.d.ts +279 -0
- package/dist/profiler/timeline.js +690 -0
- package/dist/profiler/ui/AllocStrip.svelte +146 -0
- package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
- package/dist/profiler/ui/Awaits.svelte +363 -0
- package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
- package/dist/profiler/ui/ByteStrip.svelte +182 -0
- package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
- package/dist/profiler/ui/Compare.svelte +24 -0
- package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
- package/dist/profiler/ui/CompareBody.svelte +429 -0
- package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
- package/dist/profiler/ui/ComponentsTable.svelte +265 -0
- package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
- package/dist/profiler/ui/Dashboard.svelte +492 -0
- package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
- package/dist/profiler/ui/DataRiver.svelte +206 -0
- package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
- package/dist/profiler/ui/DomTravel.svelte +207 -0
- package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
- package/dist/profiler/ui/DownloadHtml.svelte +52 -0
- package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
- package/dist/profiler/ui/Drill.svelte +276 -0
- package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
- package/dist/profiler/ui/ExportButton.svelte +59 -0
- package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
- package/dist/profiler/ui/Flame.svelte +222 -0
- package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
- package/dist/profiler/ui/FrameDetails.svelte +308 -0
- package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
- package/dist/profiler/ui/FunctionsTable.svelte +156 -0
- package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
- package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
- package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
- package/dist/profiler/ui/IslandsTable.svelte +448 -0
- package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
- package/dist/profiler/ui/KeepLocal.svelte +46 -0
- package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
- package/dist/profiler/ui/Ledger.svelte +196 -0
- package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
- package/dist/profiler/ui/LocalCompare.svelte +48 -0
- package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
- package/dist/profiler/ui/LocalReports.svelte +68 -0
- package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
- package/dist/profiler/ui/Login.svelte +13 -0
- package/dist/profiler/ui/Login.svelte.d.ts +12 -0
- package/dist/profiler/ui/LoginForm.svelte +42 -0
- package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
- package/dist/profiler/ui/Message.svelte +10 -0
- package/dist/profiler/ui/Message.svelte.d.ts +12 -0
- package/dist/profiler/ui/OnThisPage.svelte +137 -0
- package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
- package/dist/profiler/ui/OneClock.svelte +297 -0
- package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
- package/dist/profiler/ui/Panel.svelte +10 -0
- package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
- package/dist/profiler/ui/Paths.svelte +269 -0
- package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
- package/dist/profiler/ui/Patterns.svelte +364 -0
- package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
- package/dist/profiler/ui/PermalinkGate.svelte +143 -0
- package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
- package/dist/profiler/ui/RenderSteps.svelte +218 -0
- package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
- package/dist/profiler/ui/Report.svelte +28 -0
- package/dist/profiler/ui/Report.svelte.d.ts +20 -0
- package/dist/profiler/ui/ReportBody.svelte +2592 -0
- package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
- package/dist/profiler/ui/Run.svelte +17 -0
- package/dist/profiler/ui/Run.svelte.d.ts +14 -0
- package/dist/profiler/ui/RunView.svelte +146 -0
- package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
- package/dist/profiler/ui/ScoreCard.svelte +197 -0
- package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
- package/dist/profiler/ui/Scrub.svelte +293 -0
- package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
- package/dist/profiler/ui/SeedExplainer.svelte +193 -0
- package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
- package/dist/profiler/ui/ShareLink.svelte +148 -0
- package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
- package/dist/profiler/ui/Shell.svelte +50 -0
- package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
- package/dist/profiler/ui/Sidebar.svelte +312 -0
- package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
- package/dist/profiler/ui/Site.svelte +20 -0
- package/dist/profiler/ui/Site.svelte.d.ts +23 -0
- package/dist/profiler/ui/SiteView.svelte +385 -0
- package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
- package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
- package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
- package/dist/profiler/ui/Timeline.svelte +672 -0
- package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
- package/dist/profiler/ui/Treemap.svelte +278 -0
- package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
- package/dist/profiler/ui/Upload.svelte +19 -0
- package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
- package/dist/profiler/ui/UploadForm.svelte +59 -0
- package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
- package/dist/profiler/ui/WaitingTable.svelte +55 -0
- package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
- package/dist/profiler/ui/Waterfall.svelte +371 -0
- package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
- package/dist/profiler/ui/WhatIf.svelte +87 -0
- package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
- package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
- package/dist/profiler/ui/fake-progress.svelte.js +38 -0
- package/dist/profiler/ui/format.d.ts +20 -0
- package/dist/profiler/ui/format.js +71 -0
- package/dist/profiler/ui/permalink.d.ts +9 -0
- package/dist/profiler/ui/permalink.js +108 -0
- package/dist/profiler/ui/press.d.ts +5 -0
- package/dist/profiler/ui/press.js +19 -0
- package/dist/profiler/ui/profiler.css +650 -0
- package/dist/profiler/ui/report-data.d.ts +189 -0
- package/dist/profiler/ui/report-data.js +393 -0
- package/dist/profiler/ui/report-request.d.ts +6 -0
- package/dist/profiler/ui/report-request.js +50 -0
- package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
- package/dist/profiler/ui/row-anchor.svelte.js +44 -0
- package/dist/profiler/ui/sort.svelte.d.ts +14 -0
- package/dist/profiler/ui/sort.svelte.js +37 -0
- package/dist/profiler/ui/store.d.ts +45 -0
- package/dist/profiler/ui/store.js +92 -0
- package/dist/profiler/visit.d.ts +389 -0
- package/dist/profiler/visit.js +1002 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +4 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +38 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +78 -0
- package/package.json +99 -6
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<script>import { PATTERN_LABEL } from "../patterns.js";
|
|
2
|
+
import { fmt_ms, fmt_bytes } from "./format.js";
|
|
3
|
+
import { row_href } from "./row-anchor.svelte.js";
|
|
4
|
+
let { lines, patterns = [], shown = 8, renders = 1, fns } = $props();
|
|
5
|
+
// a line links to its function's row only when the functions table has that row
|
|
6
|
+
const known = $derived(fns ? new Set(fns) : null);
|
|
7
|
+
const linkable = (fn) => !!fn && (!known || known.has(fn));
|
|
8
|
+
// CPU, bytes made and GC add up every profiled render: shown per render. What a render KEPT
|
|
9
|
+
// comes from one extra render already, so it is shown as it is.
|
|
10
|
+
const per = (x) => x / Math.max(1, renders);
|
|
11
|
+
// the pattern recognised on a line, for its badge
|
|
12
|
+
const pattern_at = $derived.by(() => {
|
|
13
|
+
const m = new Map();
|
|
14
|
+
for (const p of patterns) for (const s of p.sites) m.set(s.path + ":" + s.line, p);
|
|
15
|
+
return m;
|
|
16
|
+
});
|
|
17
|
+
const top = $derived(lines.slice(0, shown));
|
|
18
|
+
const rest = $derived(lines.slice(shown));
|
|
19
|
+
const max_score = $derived(Math.max(...lines.map((l) => l.score), .01));
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
{#snippet row(l)}
|
|
23
|
+
<li class="line">
|
|
24
|
+
<div class="head">
|
|
25
|
+
<span class="bar" style="width:{Math.max(3, (l.score / max_score) * 100)}%"></span>
|
|
26
|
+
{#if linkable(l.fn)}
|
|
27
|
+
<a class="loc" href={row_href('fn:' + l.fn)} title={l.path + ':' + l.line}
|
|
28
|
+
>{l.file}:{l.line}</a
|
|
29
|
+
>
|
|
30
|
+
{:else}
|
|
31
|
+
<span class="loc" title={l.path + ':' + l.line}>{l.file}:{l.line}</span>
|
|
32
|
+
{/if}
|
|
33
|
+
{#if l.module}<span
|
|
34
|
+
class="who"
|
|
35
|
+
title="the build has no sourcemap: the line number is the built chunk's"
|
|
36
|
+
>in {l.module}</span
|
|
37
|
+
>{/if}
|
|
38
|
+
{#if l.who.length}<span class="who">{l.who.join(' · ')}</span>{/if}
|
|
39
|
+
{#if pattern_at.get(l.path + ':' + l.line)}
|
|
40
|
+
{@const p = pattern_at.get(l.path + ':' + l.line)}
|
|
41
|
+
<span class="tag" title={p.title}>{PATTERN_LABEL[p.kind]}</span>
|
|
42
|
+
{/if}
|
|
43
|
+
</div>
|
|
44
|
+
{#if l.code}<code class="code">{l.code}</code>{/if}
|
|
45
|
+
<div class="costs">
|
|
46
|
+
{#if l.cpu_ms > 0}<span class="c cpu"><b>{fmt_ms(per(l.cpu_ms))} ms</b> CPU</span>{/if}
|
|
47
|
+
{#if l.lib_ms > 0}
|
|
48
|
+
<span
|
|
49
|
+
class="c lib"
|
|
50
|
+
title={(l.libs ?? [])
|
|
51
|
+
.map(
|
|
52
|
+
(x) =>
|
|
53
|
+
`${x.name}${x.pkg ? ' (' + x.pkg + ')' : ''}: ${fmt_ms(per(x.ms))} ms${x.shared ? `, split over ${x.shared} lines that call it` : ''}`
|
|
54
|
+
)
|
|
55
|
+
.join('\n')}
|
|
56
|
+
>
|
|
57
|
+
<b>{fmt_ms(per(l.lib_ms))} ms</b> inside {(l.libs ?? [])
|
|
58
|
+
.map((x) => x.name)
|
|
59
|
+
.slice(0, 2)
|
|
60
|
+
.join(', ') || 'libraries'}
|
|
61
|
+
</span>
|
|
62
|
+
{/if}
|
|
63
|
+
{#if l.alloc_bytes > 0}<span class="c alloc"><b>{fmt_bytes(per(l.alloc_bytes))}</b> made</span
|
|
64
|
+
>{/if}
|
|
65
|
+
{#if l.gc_ms > 0}<span class="c gc"><b>{fmt_ms(per(l.gc_ms))} ms</b> cleanup</span>{/if}
|
|
66
|
+
{#if l.retained_bytes > 0}<span class="c kept"><b>{fmt_bytes(l.retained_bytes)}</b> kept</span
|
|
67
|
+
>{/if}
|
|
68
|
+
{#if l.mem_via}<span
|
|
69
|
+
class="c"
|
|
70
|
+
title="Made inside this package, which the line calls, after an await (no line of yours was running): charged to the line that calls it"
|
|
71
|
+
>memory: inside {l.mem_via}</span
|
|
72
|
+
>{/if}
|
|
73
|
+
{#if l.merged}<span
|
|
74
|
+
class="c"
|
|
75
|
+
title="V8 merged {l.merged.callee} into its caller and charged its time to line {l.merged
|
|
76
|
+
.from}. Its share was moved back here, split by a render profiled with that merging turned off."
|
|
77
|
+
>time moved from line {l.merged.from}</span
|
|
78
|
+
>{/if}
|
|
79
|
+
{#if l.mem_in_fn}<span
|
|
80
|
+
class="c"
|
|
81
|
+
title="Memory is measured per function, not per line. This function's memory is shown on its busiest line."
|
|
82
|
+
>memory: whole function</span
|
|
83
|
+
>{/if}
|
|
84
|
+
</div>
|
|
85
|
+
</li>
|
|
86
|
+
{/snippet}
|
|
87
|
+
|
|
88
|
+
<ol class="ledger">
|
|
89
|
+
{#each top as l (l.path + ':' + l.line)}{@render row(l)}{/each}
|
|
90
|
+
</ol>
|
|
91
|
+
{#if rest.length}
|
|
92
|
+
<details>
|
|
93
|
+
<summary>{rest.length} more line{rest.length === 1 ? '' : 's'}</summary>
|
|
94
|
+
<ol class="ledger">
|
|
95
|
+
{#each rest as l (l.path + ':' + l.line)}{@render row(l)}{/each}
|
|
96
|
+
</ol>
|
|
97
|
+
</details>
|
|
98
|
+
{/if}
|
|
99
|
+
|
|
100
|
+
<style>
|
|
101
|
+
.ledger {
|
|
102
|
+
list-style: none;
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
display: grid;
|
|
106
|
+
gap: 8px;
|
|
107
|
+
}
|
|
108
|
+
.line {
|
|
109
|
+
border: 1px solid var(--line);
|
|
110
|
+
border-radius: 6px;
|
|
111
|
+
background: var(--bg-sunken);
|
|
112
|
+
padding: 8px 10px;
|
|
113
|
+
min-width: 0;
|
|
114
|
+
}
|
|
115
|
+
.head {
|
|
116
|
+
position: relative;
|
|
117
|
+
display: flex;
|
|
118
|
+
flex-wrap: wrap;
|
|
119
|
+
align-items: baseline;
|
|
120
|
+
gap: 2px 10px;
|
|
121
|
+
font-size: 12.5px;
|
|
122
|
+
}
|
|
123
|
+
/* how the line ranks against the worst one, a thin rule under the heading */
|
|
124
|
+
.bar {
|
|
125
|
+
position: absolute;
|
|
126
|
+
left: 0;
|
|
127
|
+
bottom: -3px;
|
|
128
|
+
height: 2px;
|
|
129
|
+
background: var(--c-orange);
|
|
130
|
+
border-radius: 1px;
|
|
131
|
+
opacity: 0.7;
|
|
132
|
+
}
|
|
133
|
+
.loc {
|
|
134
|
+
font-family: ui-monospace, monospace;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
color: var(--text);
|
|
137
|
+
word-break: break-all;
|
|
138
|
+
text-decoration: none;
|
|
139
|
+
}
|
|
140
|
+
a.loc:hover {
|
|
141
|
+
text-decoration: underline;
|
|
142
|
+
}
|
|
143
|
+
.who {
|
|
144
|
+
color: var(--text-faint);
|
|
145
|
+
font-size: 12px;
|
|
146
|
+
font-family: ui-monospace, monospace;
|
|
147
|
+
word-break: break-all;
|
|
148
|
+
}
|
|
149
|
+
.tag {
|
|
150
|
+
font-size: 11px;
|
|
151
|
+
color: var(--c-orange);
|
|
152
|
+
border: 1px solid currentColor;
|
|
153
|
+
border-radius: 999px;
|
|
154
|
+
padding: 0 7px;
|
|
155
|
+
line-height: 1.5;
|
|
156
|
+
}
|
|
157
|
+
.code {
|
|
158
|
+
display: block;
|
|
159
|
+
margin: 7px 0 6px;
|
|
160
|
+
font-family: ui-monospace, monospace;
|
|
161
|
+
font-size: 12px;
|
|
162
|
+
color: var(--text-dim);
|
|
163
|
+
white-space: pre-wrap;
|
|
164
|
+
word-break: break-word;
|
|
165
|
+
}
|
|
166
|
+
.costs {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-wrap: wrap;
|
|
169
|
+
gap: 4px 14px;
|
|
170
|
+
font-size: 12px;
|
|
171
|
+
color: var(--text-faint);
|
|
172
|
+
}
|
|
173
|
+
.c b {
|
|
174
|
+
font-weight: 600;
|
|
175
|
+
}
|
|
176
|
+
.cpu b,
|
|
177
|
+
.lib b {
|
|
178
|
+
color: var(--c-orange);
|
|
179
|
+
}
|
|
180
|
+
.alloc b,
|
|
181
|
+
.gc b {
|
|
182
|
+
color: var(--c-blue);
|
|
183
|
+
}
|
|
184
|
+
.kept b {
|
|
185
|
+
color: var(--warn);
|
|
186
|
+
}
|
|
187
|
+
details {
|
|
188
|
+
margin-top: 8px;
|
|
189
|
+
}
|
|
190
|
+
summary {
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
font-size: 12.5px;
|
|
193
|
+
color: var(--text-dim);
|
|
194
|
+
margin-bottom: 8px;
|
|
195
|
+
}
|
|
196
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE EXACT LINES: the app lines that cost the most, one row each, with the code on the line
|
|
3
|
+
* and every cost the profiler measured joined on it (CPU, bytes allocated, the GC they caused,
|
|
4
|
+
* bytes kept alive). The tables below rank functions; this ranks the line a person edits.
|
|
5
|
+
* Static markup: the first rows show, the rest sit in a <details>.
|
|
6
|
+
*/
|
|
7
|
+
import type { LedgerLine } from '../ledger.js';
|
|
8
|
+
import { type Pattern } from '../patterns.js';
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
lines: LedgerLine[];
|
|
11
|
+
patterns?: Pattern[];
|
|
12
|
+
shown?: number;
|
|
13
|
+
renders?: number;
|
|
14
|
+
fns?: string[];
|
|
15
|
+
};
|
|
16
|
+
declare const Ledger: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type Ledger = ReturnType<typeof Ledger>;
|
|
18
|
+
export default Ledger;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script>/**
|
|
2
|
+
* COMPARE FROM THE BROWSER STORE — when the server no longer holds one of the two reports (it
|
|
3
|
+
* restarted, or it is an ephemeral instance), the compare page hands the ids to this island,
|
|
4
|
+
* which reads both dumps from this browser's store, runs the same pure comparison, and renders
|
|
5
|
+
* the same body.
|
|
6
|
+
*/
|
|
7
|
+
import { get_report } from "./store.js";
|
|
8
|
+
import { compare_reports } from "../compare.js";
|
|
9
|
+
import { derive_findings } from "../report.js";
|
|
10
|
+
import CompareBody from "./CompareBody.svelte";
|
|
11
|
+
let { base, a, b } = $props();
|
|
12
|
+
let cmp = $state(null);
|
|
13
|
+
let missing = $state([]);
|
|
14
|
+
let loaded = $state(false);
|
|
15
|
+
const pack = (d) => ({
|
|
16
|
+
meta: d.meta,
|
|
17
|
+
analysis: d.analysis,
|
|
18
|
+
findings: derive_findings(d.analysis, d.meta, d.extras).map((f) => `${f.code}: ${f.message}`),
|
|
19
|
+
...d.extras.gc_attr ? { gc: d.extras.gc_attr } : {},
|
|
20
|
+
...d.extras.patterns ? { patterns: d.extras.patterns } : {},
|
|
21
|
+
...d.extras.ledger ? { ledger: d.extras.ledger } : {},
|
|
22
|
+
...d.extras.drill ? { drill: d.extras.drill } : {},
|
|
23
|
+
...d.extras.forecast ? { forecast: d.extras.forecast } : {}
|
|
24
|
+
});
|
|
25
|
+
// read once, at setup: the two ids the compare page handed over
|
|
26
|
+
// svelte-ignore state_referenced_locally
|
|
27
|
+
void Promise.all([get_report(a), get_report(b)]).then(([A, B]) => {
|
|
28
|
+
const da = A?.dump;
|
|
29
|
+
const db = B?.dump;
|
|
30
|
+
missing = [...da ? [] : [a], ...db ? [] : [b]];
|
|
31
|
+
if (da && db) cmp = compare_reports(pack(da), pack(db));
|
|
32
|
+
}).finally(() => loaded = true);
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if cmp}
|
|
36
|
+
<CompareBody {base} {cmp} />
|
|
37
|
+
{:else if loaded}
|
|
38
|
+
<h1>Compare</h1>
|
|
39
|
+
<p class="verdict">
|
|
40
|
+
This server no longer holds {missing.length === 2 ? 'either report' : 'one of these reports'},
|
|
41
|
+
and this browser did not keep {missing.length === 2 ? 'them' : 'it'} ({missing.join(', ')}). A
|
|
42
|
+
report is kept only by the browser that recorded or opened it.
|
|
43
|
+
</p>
|
|
44
|
+
<p class="hint"><a href={base}>← dashboard</a></p>
|
|
45
|
+
{:else}
|
|
46
|
+
<h1>Compare</h1>
|
|
47
|
+
<p class="hint">Reading both reports from this browser…</p>
|
|
48
|
+
{/if}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script>/**
|
|
2
|
+
* KEPT IN THIS BROWSER — the reports this browser holds in its own store (ui/store.ts), listed
|
|
3
|
+
* on the dashboard next to the server's. On an ephemeral host this is THE list: the server's is
|
|
4
|
+
* empty a minute later. Each opens at its `/report/<id>` (the page falls back to the store when
|
|
5
|
+
* the server has forgotten it); two of the same page compare.
|
|
6
|
+
*/
|
|
7
|
+
import { list_reports, delete_report } from "./store.js";
|
|
8
|
+
import { fmt_ms } from "./format.js";
|
|
9
|
+
let { base, server_ids = [] } = $props();
|
|
10
|
+
let rows = $state([]);
|
|
11
|
+
let loaded = $state(false);
|
|
12
|
+
const on_server = $derived(new Set(server_ids));
|
|
13
|
+
void list_reports().then((r) => rows = r).finally(() => loaded = true);
|
|
14
|
+
const when = (ms) => new Date(ms).toLocaleString();
|
|
15
|
+
/** the previous kept report of the same page, for a compare link */
|
|
16
|
+
const prev_of = (r) => rows.find((o) => o.page === r.page && o.at < r.at && o.trigger === "page");
|
|
17
|
+
async function forget(id) {
|
|
18
|
+
await delete_report(id);
|
|
19
|
+
rows = rows.filter((r) => r.id !== id);
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
{#if loaded && rows.length}
|
|
24
|
+
<h2>
|
|
25
|
+
Kept in this browser <span class="hint" style="font-weight:400"
|
|
26
|
+
>({rows.length} — reports this browser stored itself; they outlive the server)</span
|
|
27
|
+
>
|
|
28
|
+
</h2>
|
|
29
|
+
<table>
|
|
30
|
+
<thead
|
|
31
|
+
><tr
|
|
32
|
+
><th>when</th><th>page</th><th class="num">median</th><th>on this server</th><th
|
|
33
|
+
><span class="sr-only">actions</span></th
|
|
34
|
+
></tr
|
|
35
|
+
></thead
|
|
36
|
+
>
|
|
37
|
+
<tbody>
|
|
38
|
+
{#each rows as r (r.id)}
|
|
39
|
+
{@const prev = prev_of(r)}
|
|
40
|
+
<tr>
|
|
41
|
+
<td class="file">{when(r.at)}</td>
|
|
42
|
+
<td class="fn"
|
|
43
|
+
><a href="{base}/report/{r.id}">{r.label}</a> <span class="hint">{r.trigger}</span></td
|
|
44
|
+
>
|
|
45
|
+
<td class="num">{r.median !== null ? `${fmt_ms(r.median)} ms` : '—'}</td>
|
|
46
|
+
<td class="file">{on_server.has(r.id) ? 'yes' : 'no (browser only)'}</td>
|
|
47
|
+
<td class="file">
|
|
48
|
+
{#if prev}<a href="{base}/compare/{prev.id}/{r.id}">compare with previous</a> ·
|
|
49
|
+
{/if}
|
|
50
|
+
<button class="link" onclick={() => forget(r.id)}>forget</button>
|
|
51
|
+
</td>
|
|
52
|
+
</tr>
|
|
53
|
+
{/each}
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
{/if}
|
|
57
|
+
|
|
58
|
+
<style>
|
|
59
|
+
.link {
|
|
60
|
+
background: none;
|
|
61
|
+
border: 0;
|
|
62
|
+
color: inherit;
|
|
63
|
+
text-decoration: underline;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
font: inherit;
|
|
66
|
+
padding: 0;
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script>/** The locked-profiler gate. Static shell + the reactive LoginForm island. Ports render_login. */
|
|
2
|
+
import Shell from "./Shell.svelte";
|
|
3
|
+
import LoginForm from "./LoginForm.svelte" with { wake: "load" };
|
|
4
|
+
let { data } = $props();
|
|
5
|
+
const { base, next, session_problem } = $derived(data);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Shell {base} bare>
|
|
9
|
+
<h1>ogygia profiler <small>locked</small></h1>
|
|
10
|
+
<!-- A login that succeeded but whose session never reached the server: say why, don't loop. -->
|
|
11
|
+
{#if session_problem}<p class="verdict" data-session-problem>{session_problem}</p>{/if}
|
|
12
|
+
<LoginForm {base} {next} />
|
|
13
|
+
</Shell>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const Login: import("svelte").Component<{
|
|
2
|
+
data: {
|
|
3
|
+
base: string;
|
|
4
|
+
next: string;
|
|
5
|
+
session_problem: string | null;
|
|
6
|
+
};
|
|
7
|
+
form: Response | null;
|
|
8
|
+
params: {};
|
|
9
|
+
search: {};
|
|
10
|
+
}, {}, "">;
|
|
11
|
+
type Login = ReturnType<typeof Login>;
|
|
12
|
+
export default Login;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script>/**
|
|
2
|
+
* The unlock form — a `wake: 'load'` island (real reactive state, was report.ts's inline login
|
|
3
|
+
* script). Posts the key as JSON (NOT a form POST) so Kit's CSRF — which only guards form
|
|
4
|
+
* content-types and would otherwise need the app's ORIGIN set — never applies; the session cookie
|
|
5
|
+
* rides the response. On success it navigates to `next`.
|
|
6
|
+
*/
|
|
7
|
+
let { base, next } = $props();
|
|
8
|
+
let key = $state("");
|
|
9
|
+
let wrong = $state(false);
|
|
10
|
+
async function submit(ev) {
|
|
11
|
+
ev.preventDefault();
|
|
12
|
+
wrong = false;
|
|
13
|
+
try {
|
|
14
|
+
const r = await fetch(base + "/login", {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: { "content-type": "application/json" },
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
key,
|
|
19
|
+
next
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
if (r.ok) {
|
|
23
|
+
const d = await r.json();
|
|
24
|
+
location.href = d.next || base;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
wrong = true;
|
|
29
|
+
key = "";
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<p class="hint">Enter the profiler key to continue. It's kept for this browser session.</p>
|
|
34
|
+
{#if wrong}<p class="verdict">Wrong key.</p>{/if}
|
|
35
|
+
<form class="inline" onsubmit={submit}>
|
|
36
|
+
<label>
|
|
37
|
+
key
|
|
38
|
+
<!-- svelte-ignore a11y_autofocus -->
|
|
39
|
+
<input type="password" bind:value={key} autofocus size="34" autocomplete="current-password" />
|
|
40
|
+
</label>
|
|
41
|
+
<button class="btn primary">Unlock</button>
|
|
42
|
+
</form>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script>/** The router's error boundary (Kit's +error.svelte) for the profiler — renders any c.error(). Static. */
|
|
2
|
+
import Shell from "./Shell.svelte";
|
|
3
|
+
let { status, error, data } = $props();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<Shell base={data.base}>
|
|
7
|
+
<h1>Error {status}</h1>
|
|
8
|
+
<p>{error.message}</p>
|
|
9
|
+
<p><a href={data.base}>← dashboard</a></p>
|
|
10
|
+
</Shell>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
status: number;
|
|
3
|
+
error: {
|
|
4
|
+
message: string;
|
|
5
|
+
};
|
|
6
|
+
data: {
|
|
7
|
+
base: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
declare const Message: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type Message = ReturnType<typeof Message>;
|
|
12
|
+
export default Message;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script>let entries = $state([]);
|
|
2
|
+
let active = $state(0);
|
|
3
|
+
$effect(() => {
|
|
4
|
+
const els = [...document.querySelectorAll(".part, .app-main h2")];
|
|
5
|
+
entries = els.map((el) => {
|
|
6
|
+
if (el.classList.contains("part")) return {
|
|
7
|
+
n: el.querySelector(".part-n")?.textContent ?? "",
|
|
8
|
+
t: el.querySelector(".part-t")?.textContent ?? "",
|
|
9
|
+
el,
|
|
10
|
+
level: 0
|
|
11
|
+
};
|
|
12
|
+
// an h2's own text, minus the trailing "(hint …)" the sections carry
|
|
13
|
+
const t = (el.childNodes[0]?.textContent ?? el.textContent ?? "").trim();
|
|
14
|
+
return {
|
|
15
|
+
n: "",
|
|
16
|
+
t,
|
|
17
|
+
el,
|
|
18
|
+
level: 1
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
if (!els.length) return;
|
|
22
|
+
const io = new IntersectionObserver((ents) => {
|
|
23
|
+
for (const e of ents) {
|
|
24
|
+
if (e.isIntersecting) {
|
|
25
|
+
const i = els.indexOf(e.target);
|
|
26
|
+
if (i >= 0) active = i;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}, {
|
|
30
|
+
rootMargin: "-6% 0px -85% 0px",
|
|
31
|
+
threshold: 0
|
|
32
|
+
});
|
|
33
|
+
for (const el of els) io.observe(el);
|
|
34
|
+
// the report reserves the rail's space server-side (Shell `toc`); nothing to toggle here
|
|
35
|
+
return () => io.disconnect();
|
|
36
|
+
});
|
|
37
|
+
const jump = (e) => e.el.scrollIntoView({
|
|
38
|
+
behavior: "smooth",
|
|
39
|
+
block: "start"
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
{#if entries.length > 1}
|
|
44
|
+
<nav class="otp" aria-label="on this page">
|
|
45
|
+
<div class="otp-h">On this page</div>
|
|
46
|
+
<ul>
|
|
47
|
+
{#each entries as e, i (i)}
|
|
48
|
+
<li>
|
|
49
|
+
<button
|
|
50
|
+
class:on={i === active}
|
|
51
|
+
class:part={e.level === 0}
|
|
52
|
+
class:sub={e.level === 1}
|
|
53
|
+
onclick={() => jump(e)}
|
|
54
|
+
>
|
|
55
|
+
{#if e.n}<span class="n">{e.n}</span>{/if}{e.t}
|
|
56
|
+
</button>
|
|
57
|
+
</li>
|
|
58
|
+
{/each}
|
|
59
|
+
</ul>
|
|
60
|
+
</nav>
|
|
61
|
+
{/if}
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.otp {
|
|
65
|
+
position: fixed;
|
|
66
|
+
right: 16px;
|
|
67
|
+
top: 22px;
|
|
68
|
+
width: 200px;
|
|
69
|
+
max-height: calc(100vh - 40px);
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
z-index: 8;
|
|
72
|
+
}
|
|
73
|
+
.otp-h {
|
|
74
|
+
font-size: 10px;
|
|
75
|
+
text-transform: uppercase;
|
|
76
|
+
letter-spacing: 0.06em;
|
|
77
|
+
color: var(--text-faint);
|
|
78
|
+
padding: 0 0 4px;
|
|
79
|
+
}
|
|
80
|
+
.otp ul {
|
|
81
|
+
list-style: none;
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding: 0;
|
|
84
|
+
}
|
|
85
|
+
.otp button {
|
|
86
|
+
display: flex;
|
|
87
|
+
gap: 7px;
|
|
88
|
+
align-items: baseline;
|
|
89
|
+
width: 100%;
|
|
90
|
+
text-align: left;
|
|
91
|
+
background: none;
|
|
92
|
+
border: 0;
|
|
93
|
+
padding: 2px 0;
|
|
94
|
+
color: var(--text-faint);
|
|
95
|
+
font: inherit;
|
|
96
|
+
font-size: 12px;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
line-height: 1.3;
|
|
99
|
+
}
|
|
100
|
+
.otp button:hover {
|
|
101
|
+
color: var(--text);
|
|
102
|
+
}
|
|
103
|
+
/* the current section: text goes accent + bold, no side marker */
|
|
104
|
+
.otp button.on {
|
|
105
|
+
color: var(--accent-strong);
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
}
|
|
108
|
+
.otp button.part.on {
|
|
109
|
+
color: var(--accent-strong);
|
|
110
|
+
}
|
|
111
|
+
.otp button.part {
|
|
112
|
+
color: var(--text);
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
margin-top: 5px;
|
|
115
|
+
}
|
|
116
|
+
.otp button.part:first-child {
|
|
117
|
+
margin-top: 0;
|
|
118
|
+
}
|
|
119
|
+
.otp button.sub {
|
|
120
|
+
padding-left: 12px;
|
|
121
|
+
font-size: 11.5px;
|
|
122
|
+
}
|
|
123
|
+
.otp .n {
|
|
124
|
+
font-family: var(--font-mono);
|
|
125
|
+
font-size: 10px;
|
|
126
|
+
color: var(--text-faint);
|
|
127
|
+
}
|
|
128
|
+
.otp button.on .n {
|
|
129
|
+
color: var(--accent);
|
|
130
|
+
}
|
|
131
|
+
/* only show when there's room to the right of the content column */
|
|
132
|
+
@media (max-width: 1500px) {
|
|
133
|
+
.otp {
|
|
134
|
+
display: none;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
</style>
|