buninu-browser 0.2.4 → 0.4.0
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/LICENSES/jsmdcui.txt +22 -0
- package/README.md +102 -2
- package/frontends/wayland/LICENSE-land +21 -0
- package/frontends/wayland/README.md +123 -0
- package/frontends/wayland/casty/LICENSE +21 -0
- package/frontends/wayland/casty/bookmarks.js +32 -0
- package/frontends/wayland/casty/click.js +96 -0
- package/frontends/wayland/casty/commands.js +61 -0
- package/frontends/wayland/casty/config.js +45 -0
- package/frontends/wayland/casty/download-directory.js +28 -0
- package/frontends/wayland/casty/download.js +49 -0
- package/frontends/wayland/casty/hints.js +334 -0
- package/frontends/wayland/casty/keys.js +56 -0
- package/frontends/wayland/casty/page-commands.js +80 -0
- package/frontends/wayland/casty/prompt-width.js +148 -0
- package/frontends/wayland/casty/urlbar.js +606 -0
- package/frontends/wayland/client.js +88 -0
- package/frontends/wayland/clipboard.js +80 -0
- package/frontends/wayland/index.js +116 -0
- package/frontends/wayland/input.js +56 -0
- package/frontends/wayland/protocol.js +55 -0
- package/frontends/wayland/protocols/wayland.xml +3248 -0
- package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
- package/frontends/wayland/transport.js +120 -0
- package/frontends/wayland/urlbar.js +158 -0
- package/index.js +28 -1
- package/lib/clipboard-command.js +10 -0
- package/lib/clipboard-powershell.js +22 -0
- package/lib/clipboard.js +57 -0
- package/lib/document-types.js +11 -0
- package/lib/headless-shell.js +78 -13
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +5 -1
- package/lib/paint/frame.js +35 -3
- package/lib/paint/raw-frames.js +47 -0
- package/lib/qml/README.md +971 -0
- package/lib/qml/builtins.js +700 -0
- package/lib/qml/canvas-dom.js +115 -0
- package/lib/qml/document.js +1176 -0
- package/lib/qml/format.js +9 -0
- package/lib/qml/host.js +324 -0
- package/lib/qml/images.js +199 -0
- package/lib/qml/launcher.js +132 -0
- package/lib/qml/loader.js +517 -0
- package/lib/qml/media-renderer.js +49 -0
- package/lib/qml/media.js +86 -0
- package/lib/qml/models.js +622 -0
- package/lib/qml/parser.js +381 -0
- package/lib/qml/qs +5 -0
- package/lib/qml/qscasty +3 -0
- package/lib/qml/qsheadless +3 -0
- package/lib/qml/qswayland +3 -0
- package/lib/qml/reactive.js +159 -0
- package/lib/qml/runtime.js +3381 -0
- package/lib/qml/shapes.js +243 -0
- package/lib/qml/text.js +109 -0
- package/lib/qml/textures.js +123 -0
- package/lib/qml/wayland-clipboard.js +32 -0
- package/lib/qml/worker-thread.js +70 -0
- package/lib/render-tree/index.js +16 -1
- package/lib/renderer/bindings.js +11 -0
- package/lib/renderer/canvas-2d.js +13 -0
- package/lib/renderer/compositor-channel.js +103 -0
- package/lib/renderer/compositor.js +25 -33
- package/lib/renderer/flags.js +1 -1
- package/lib/renderer/host.js +128 -92
- package/lib/renderer/main.js +38 -4
- package/lib/renderer/page-realm.js +32 -1
- package/lib/renderer/page-renderer.js +250 -21
- package/lib/renderer/text-input-paint.js +54 -0
- package/lib/renderer/text-input.js +57 -0
- package/lib/style/computed-style.js +2 -2
- package/package.json +1 -1
- package/test/clipboard.test.js +54 -0
- package/test/fixtures/browser-raw-demo.html +36 -0
- package/test/fixtures/flea-helpers/Dockerfile +36 -0
- package/test/fixtures/flea-helpers/bat.mjs +43 -0
- package/test/fixtures/flea-helpers/bin/bat +3 -0
- package/test/fixtures/flea-helpers/bin/bwrap +10 -0
- package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
- package/test/fixtures/flea-helpers/bin/glow +3 -0
- package/test/fixtures/flea-helpers/bin/qs +3 -0
- package/test/fixtures/flea-helpers/bin/unsupported +3 -0
- package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
- package/test/fixtures/flea-helpers/check-media.mjs +29 -0
- package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
- package/test/fixtures/flea-helpers/check.mjs +36 -0
- package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
- package/test/fixtures/flea-helpers/source-mime.xml +28 -0
- package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
- package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
- package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
- package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea/LICENSE +21 -0
- package/test/fixtures/qml/flea/README.md +25 -0
- package/test/fixtures/qml/flea/Theme.qml +8 -0
- package/test/fixtures/qml/flea/demo.qml +24 -0
- package/test/fixtures/qml/flea/qmldir +3 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
- package/test/fixtures/qml/flea-theme/README.md +70 -0
- package/test/fixtures/qml/flea-theme/button.qml +17 -0
- package/test/fixtures/qml/flea-theme/demo.qml +27 -0
- package/test/fixtures/qml/flea-theme/field.qml +21 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
- package/test/fixtures/qml/flea-theme/segment.qml +17 -0
- package/test/fixtures/qml/flea-theme/settings.qml +58 -0
- package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
- package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
- package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
- package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
- package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
- package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
- package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
- package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
- package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
- package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
- package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
- package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
- package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
- package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
- package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
- package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
- package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
- package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
- package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
- package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
- package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
- package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
- package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
- package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
- package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
- package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
- package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
- package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
- package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
- package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
- package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
- package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
- package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
- package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
- package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
- package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
- package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
- package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
- package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
- package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
- package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
- package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
- package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
- package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
- package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
- package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
- package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
- package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
- package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
- package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
- package/test/fixtures/qml/hello.qml +26 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
- package/test/fixtures/qml/imports/components/Badge.qml +15 -0
- package/test/fixtures/qml/imports/logic/base.js +2 -0
- package/test/fixtures/qml/imports/logic/counter.js +4 -0
- package/test/fixtures/qml/imports/logic/math.js +3 -0
- package/test/fixtures/qml/imports/logic/shared.js +3 -0
- package/test/fixtures/qml/imports/main.qml +21 -0
- package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
- package/test/fixtures/qml/imports/theme/qmldir +2 -0
- package/test/fixtures/qml/multiply.qml +61 -0
- package/test/fixtures/qml-canvas-dom.js +90 -0
- package/test/fixtures/qml-media-audio.js +39 -0
- package/test/fixtures/raw-capture-benchmark.json +76 -0
- package/test/helpers/flea-sandbox.js +48 -0
- package/test/helpers/quickshell-fixture.js +125 -0
- package/test/html-clipboard-wayland.test.js +47 -0
- package/test/index.test.js +13 -0
- package/test/manual-casty-without-qml.js +87 -0
- package/test/manual-flea-application.js +49 -0
- package/test/manual-flea-binary.js +62 -0
- package/test/manual-flea-qml.js +9 -0
- package/test/manual-flea-settings.js +40 -0
- package/test/manual-flea-theme.js +40 -0
- package/test/manual-qml-removal.js +46 -0
- package/test/manual-without-qml.js +45 -0
- package/test/qml-canvas-dom.test.js +37 -0
- package/test/qml-clipboard-bridge.test.js +25 -0
- package/test/qml-clipboard-wayland.test.js +40 -0
- package/test/qml-connections-timing.test.js +73 -0
- package/test/qml-dynamic.test.js +401 -0
- package/test/qml-flea-app.test.js +146 -0
- package/test/qml-flea-backend.test.js +78 -0
- package/test/qml-flea-columns-backend.test.js +38 -0
- package/test/qml-flea-compile.test.js +78 -0
- package/test/qml-flea-launcher.test.js +88 -0
- package/test/qml-flea-markdown-backend.test.js +31 -0
- package/test/qml-flea-media.test.js +28 -0
- package/test/qml-flea-navigation-backend.test.js +35 -0
- package/test/qml-flea-navigation-timing.test.js +147 -0
- package/test/qml-flea-preview-backend.test.js +53 -0
- package/test/qml-flea-previews.test.js +163 -0
- package/test/qml-flea-rows.test.js +101 -0
- package/test/qml-flea-textures.test.js +59 -0
- package/test/qml-flea-view-switch.test.js +33 -0
- package/test/qml-flea-wayland.test.js +68 -0
- package/test/qml-flea.test.js +34 -0
- package/test/qml-host.test.js +238 -0
- package/test/qml-imports.test.js +102 -0
- package/test/qml-inherited-handlers.test.js +51 -0
- package/test/qml-input-selection.test.js +66 -0
- package/test/qml-interactions.test.js +309 -0
- package/test/qml-language.test.js +167 -0
- package/test/qml-launcher.test.js +54 -0
- package/test/qml-layout-text.test.js +205 -0
- package/test/qml-media.test.js +63 -0
- package/test/qml-persisted-delegates.test.js +45 -0
- package/test/qml-quickshell-controls.test.js +65 -0
- package/test/qml-quickshell-settings.test.js +62 -0
- package/test/qml-repeater.test.js +72 -0
- package/test/qml-style-updates.test.js +124 -0
- package/test/qml-textures.test.js +66 -0
- package/test/qml-views.test.js +332 -0
- package/test/qml-worker.test.js +69 -0
- package/test/qml.test.js +83 -0
- package/test/raw-capture.test.js +152 -0
- package/test/renderer-host.test.js +11 -0
- package/test/text-input-pointer.test.js +31 -0
- package/test/text-input-scroll.test.js +29 -0
- package/test/text-input.test.js +47 -0
- package/test/wayland-frontend.test.js +83 -0
- package/test/wayland-urlbar.test.js +38 -0
- package/test/window-resize.test.js +36 -0
- package/tools/bench-raw-capture.js +32 -0
|
@@ -236,9 +236,9 @@ function blockChildren(node, flags) {
|
|
|
236
236
|
children.push(buildBox(child, inheritedFlags(child, childFlags)));
|
|
237
237
|
for (const open of openInlines) items.push({ kind: "open", node: open, style: open.style ?? null });
|
|
238
238
|
} else if (child.type === "image") {
|
|
239
|
-
items.push({ kind: "image", node: child });
|
|
239
|
+
items.push({ kind: "image", node: child, wrap:childFlags.wrap });
|
|
240
240
|
} else if (isAtomic(child)) {
|
|
241
|
-
items.push({ kind: "atomic", node: child, box: buildBox(child, inheritedFlags(child, childFlags)) });
|
|
241
|
+
items.push({ kind: "atomic", node: child, wrap:childFlags.wrap, box: buildBox(child, inheritedFlags(child, childFlags)) });
|
|
242
242
|
} else {
|
|
243
243
|
items.push({ kind: "open", node: child, style: child.style ?? null });
|
|
244
244
|
openInlines.push(child);
|
|
@@ -2117,11 +2117,13 @@ function buildParagraph(ctx, ifc) {
|
|
|
2117
2117
|
let value = String(item.text).replace(/\r\n?/g, "\n");
|
|
2118
2118
|
if (item.preserve) {
|
|
2119
2119
|
const pieces = value.split("\n");
|
|
2120
|
+
let sourceStart=0;
|
|
2120
2121
|
pieces.forEach((piece, index) => {
|
|
2121
2122
|
if (index > 0) forced.set(text.length, (forced.get(text.length) ?? 0) + 1);
|
|
2122
2123
|
if (piece) {
|
|
2123
|
-
pushRun({ kind: "text", nodeId: item.node.id, style: item.style, collapsible: false, wrap: item.wrap }, piece);
|
|
2124
|
+
pushRun({ kind: "text", nodeId: item.node.id, style: item.style, collapsible: false, wrap: item.wrap, sourceStart }, piece);
|
|
2124
2125
|
}
|
|
2126
|
+
sourceStart+=piece.length+1;
|
|
2125
2127
|
});
|
|
2126
2128
|
afterSpace = false;
|
|
2127
2129
|
} else {
|
|
@@ -2137,7 +2139,7 @@ function buildParagraph(ctx, ifc) {
|
|
|
2137
2139
|
forced.set(text.length, (forced.get(text.length) ?? 0) + 1);
|
|
2138
2140
|
afterSpace = true;
|
|
2139
2141
|
} else if (item.kind === "image" || item.kind === "atomic") {
|
|
2140
|
-
pushRun({ kind: item.kind, nodeId: item.node.id, style: item.node.style ?? null, node: item.node, box: item.box, wrap:
|
|
2142
|
+
pushRun({ kind: item.kind, nodeId: item.node.id, style: item.node.style ?? null, node: item.node, box: item.box, wrap: item.wrap!==false }, "");
|
|
2141
2143
|
afterSpace = false;
|
|
2142
2144
|
}
|
|
2143
2145
|
}
|
|
@@ -2158,7 +2160,7 @@ function buildParagraph(ctx, ifc) {
|
|
|
2158
2160
|
const end = index + segment.length;
|
|
2159
2161
|
if (!run) continue;
|
|
2160
2162
|
if (run.collapsible && run.wrap && segment === " ") opportunities.add(end);
|
|
2161
|
-
else if (run.kind === "image" || run.kind === "atomic") {
|
|
2163
|
+
else if (run.wrap && (run.kind === "image" || run.kind === "atomic")) {
|
|
2162
2164
|
opportunities.add(index);
|
|
2163
2165
|
opportunities.add(end);
|
|
2164
2166
|
} else if (run.wrap && run.kind === "text" && (Bun.stringWidth(segment) > 1 || (!run.collapsible && segment === " "))) {
|
|
@@ -2404,7 +2406,7 @@ function placeLine(ctx, ifc, paragraph, line, x, width, y, atomFor) {
|
|
|
2404
2406
|
ascent = Math.max(ascent, atomAscent);
|
|
2405
2407
|
descent = Math.max(descent, atom.marginTop + atom.height + atom.marginBottom - atomAscent);
|
|
2406
2408
|
} else {
|
|
2407
|
-
segment.metrics = textMetrics(ctx, run.style);
|
|
2409
|
+
segment.metrics = textMetrics(ctx, run.style, paragraph.text.slice(segment.start,segment.end));
|
|
2408
2410
|
ascent = Math.max(ascent, segment.metrics.ascent);
|
|
2409
2411
|
descent = Math.max(descent, segment.metrics.descent);
|
|
2410
2412
|
}
|
|
@@ -2461,6 +2463,7 @@ function placeLine(ctx, ifc, paragraph, line, x, width, y, atomFor) {
|
|
|
2461
2463
|
const common = {
|
|
2462
2464
|
nodeId: run.nodeId,
|
|
2463
2465
|
text: run.kind === "text" ? paragraph.text.slice(segment.start, segment.end) : "",
|
|
2466
|
+
sourceStart:(run.sourceStart??0)+segment.start-run.start,
|
|
2464
2467
|
start: segment.start - line.start,
|
|
2465
2468
|
end: segment.end - line.start,
|
|
2466
2469
|
style: run.style ?? style,
|
|
@@ -2598,8 +2601,8 @@ function measureAtomic(ctx, box, availableWidth) {
|
|
|
2598
2601
|
};
|
|
2599
2602
|
}
|
|
2600
2603
|
|
|
2601
|
-
function textMetrics(ctx, style) {
|
|
2602
|
-
const font = ctx.metrics(style);
|
|
2604
|
+
function textMetrics(ctx, style, text = null) {
|
|
2605
|
+
const font = ctx.metrics(style,text);
|
|
2603
2606
|
// `line-height: normal` (null) uses the font's own content height.
|
|
2604
2607
|
const lineHeight = ctx.lineHeight ?? (style ? style.lineHeight ?? font.height : ctx.fallbackLineHeight);
|
|
2605
2608
|
const halfLeading = (lineHeight - font.height) / 2;
|
|
@@ -2668,7 +2671,12 @@ function cachedMetrics(fontMetrics) {
|
|
|
2668
2671
|
}
|
|
2669
2672
|
return metrics;
|
|
2670
2673
|
};
|
|
2671
|
-
return (style) => {
|
|
2674
|
+
return (style,text=null) => {
|
|
2675
|
+
// Fallback fonts can be taller than the Latin strut. Use the same glyphs
|
|
2676
|
+
// measured for this run rather than assuming every character uses "Hg".
|
|
2677
|
+
if(text && /[^\x00-\x7f]/.test(text) && typeof fontMetrics==='function') {
|
|
2678
|
+
const measured=fontMetrics(style,text);if(measured?.height>0)return measured;
|
|
2679
|
+
}
|
|
2672
2680
|
if (style === null || typeof style !== "object") return metricsFor(style);
|
|
2673
2681
|
let metrics = byStyle.get(style);
|
|
2674
2682
|
if (!metrics) byStyle.set(style, metrics = metricsFor(style));
|
|
@@ -193,6 +193,9 @@ export function buildDisplayList(layout, tree = null, resources = null, backgrou
|
|
|
193
193
|
rect: effect.transform ? transformRect(effect.transform, hitRect) : hitRect,
|
|
194
194
|
clipRect: effect.clipRect,
|
|
195
195
|
fixed: effect.fixed,
|
|
196
|
+
// Preserve text geometry for form-control caret hit testing, including
|
|
197
|
+
// whitespace runs which intentionally have no painted glyphs.
|
|
198
|
+
...(tree?.nodesById.get(run.nodeId)?.pseudo==='value'?{textRun:run,transform:effect.transform}:{}),
|
|
196
199
|
});
|
|
197
200
|
reach(effect, hitRect);
|
|
198
201
|
paintRun(fragment, run);
|
|
@@ -234,6 +237,7 @@ export function buildDisplayList(layout, tree = null, resources = null, backgrou
|
|
|
234
237
|
op: "drawText",
|
|
235
238
|
nodeId: run.nodeId,
|
|
236
239
|
text: run.text,
|
|
240
|
+
textWidth: run.width,
|
|
237
241
|
x: run.x,
|
|
238
242
|
y,
|
|
239
243
|
font: {
|
|
@@ -411,7 +415,7 @@ function paintEffects(layout, tree, scrollOffsetOf = null) {
|
|
|
411
415
|
radii = insetRadii(radii, style.borderWidths ?? [0, 0, 0, 0]);
|
|
412
416
|
}
|
|
413
417
|
clips.push([node, clip, radii]);
|
|
414
|
-
if (isScroller(style)) {
|
|
418
|
+
if (isScroller(style) && boxes.has(node.id)) {
|
|
415
419
|
if (!scrolledBy.size) outOfFlow = positionedOut;
|
|
416
420
|
scrolledBy.add(node);
|
|
417
421
|
}
|
package/lib/paint/frame.js
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* modify them, including while an asynchronous image encoder is using them.
|
|
16
16
|
* @returns {{ pixels: Uint8Array, width: number, height: number }}
|
|
17
17
|
*/
|
|
18
|
-
export function cropViewport(tile, scrollX, scrollY, viewportWidth, viewportHeight) {
|
|
18
|
+
export function cropViewport(tile, scrollX, scrollY, viewportWidth, viewportHeight, exact = false) {
|
|
19
19
|
const scale = tile.scale;
|
|
20
|
-
const width = Math.max(1, Math.round(Math.floor(viewportWidth) * scale));
|
|
21
|
-
const height = Math.max(1, Math.round(Math.floor(viewportHeight) * scale));
|
|
20
|
+
const width = Math.max(1, Math.round((exact ? viewportWidth : Math.floor(viewportWidth)) * scale));
|
|
21
|
+
const height = Math.max(1, Math.round((exact ? viewportHeight : Math.floor(viewportHeight)) * scale));
|
|
22
22
|
const offsetX = Math.round((scrollX - tile.x) * scale);
|
|
23
23
|
const offsetY = Math.round((scrollY - tile.y) * scale);
|
|
24
24
|
// Raster tiles are normally viewport-wide. A full-width crop is already
|
|
@@ -115,3 +115,35 @@ export function backgroundBytes(color) {
|
|
|
115
115
|
if (!match) return [255, 255, 255, 255];
|
|
116
116
|
return [Number(match[1]), Number(match[2]), Number(match[3]), Math.round(Number(match[4] ?? 1) * 255)];
|
|
117
117
|
}
|
|
118
|
+
|
|
119
|
+
/** Raw screenshot payload, owned independently of a recyclable raster tile. */
|
|
120
|
+
export function rawFrame(pixels, width, height, format) {
|
|
121
|
+
if (!['rgba', 'bgra'].includes(format) || width * height > 16777216) throw Error('Invalid raw screenshot');
|
|
122
|
+
const data = new Uint8Array(pixels);
|
|
123
|
+
if (format === 'bgra') {
|
|
124
|
+
for (let i=0; i<data.length; i+=4) { const red=data[i]; data[i]=data[i+2]; data[i+2]=red; }
|
|
125
|
+
}
|
|
126
|
+
return {format, width, height, pixels:data};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// CDP clip coordinates are document CSS pixels, not viewport-relative pixels.
|
|
130
|
+
export function captureViewport(tile, x, y, width, height, clip) {
|
|
131
|
+
if (!clip) return cropViewport(tile,x,y,width,height);
|
|
132
|
+
if (![clip.x,clip.y,clip.width,clip.height,clip.scale].every(Number.isFinite)
|
|
133
|
+
|| clip.width<=0 || clip.height<=0 || clip.scale<=0 || clip.scale>8)
|
|
134
|
+
throw Error('Invalid screenshot clip');
|
|
135
|
+
const w=Math.max(1,Math.round(clip.width*tile.scale*clip.scale));
|
|
136
|
+
const h=Math.max(1,Math.round(clip.height*tile.scale*clip.scale));
|
|
137
|
+
const sourceW=Math.max(1,Math.round(clip.width*tile.scale));
|
|
138
|
+
const sourceH=Math.max(1,Math.round(clip.height*tile.scale));
|
|
139
|
+
if(Math.max(w,h,sourceW,sourceH)>8192 || w*h>16777216 || sourceW*sourceH>16777216)
|
|
140
|
+
throw Error('Invalid screenshot clip size');
|
|
141
|
+
const frame=cropViewport(tile,clip.x,clip.y,clip.width,clip.height,true);
|
|
142
|
+
if(clip.scale===1)return frame;
|
|
143
|
+
const pixels=new Uint8Array(w*h*4);
|
|
144
|
+
for(let row=0;row<h;row++)for(let col=0;col<w;col++) {
|
|
145
|
+
const from=(Math.min(frame.height-1,Math.floor(row/clip.scale))*frame.width+Math.min(frame.width-1,Math.floor(col/clip.scale)))*4;
|
|
146
|
+
pixels.set(frame.pixels.subarray(from,from+4),(row*w+col)*4);
|
|
147
|
+
}
|
|
148
|
+
return {pixels,width:w,height:h};
|
|
149
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {mkdtempSync,writeFileSync,readdirSync,statSync,unlinkSync,rmSync,realpathSync} from 'node:fs';
|
|
2
|
+
import {join} from 'node:path';
|
|
3
|
+
import {tmpdir} from 'node:os';
|
|
4
|
+
|
|
5
|
+
// The producer owns the directory; Kitty t=t consumers unlink individual frames.
|
|
6
|
+
// A worker borrows a directory owned by its parent, so terminating the worker
|
|
7
|
+
// cannot skip cleanup. No pixel data is encoded into the CDP response.
|
|
8
|
+
export class RawFrames {
|
|
9
|
+
constructor({directory,ownsDirectory=true}={}) {
|
|
10
|
+
this.externalDirectory=directory;this.ownsDirectory=ownsDirectory;
|
|
11
|
+
this.sequence=0;this.closed=false;
|
|
12
|
+
this.onExit=()=>this.close();
|
|
13
|
+
}
|
|
14
|
+
get directory() {
|
|
15
|
+
if(this.closed)throw Error('Raw frame store is closed');
|
|
16
|
+
if(!this.path){
|
|
17
|
+
this.path=this.externalDirectory ? this.externalDirectory() : realpathSync(mkdtempSync(join(tmpdir(),'tty-graphics-protocol-browser-')));
|
|
18
|
+
if(!this.path)throw Error('Missing raw frame directory');
|
|
19
|
+
if(this.ownsDirectory)process.once('exit',this.onExit);
|
|
20
|
+
}
|
|
21
|
+
return this.path;
|
|
22
|
+
}
|
|
23
|
+
write(bytes,width,height,format) {
|
|
24
|
+
if (!['rgba','bgra'].includes(format) || !Number.isInteger(width) || !Number.isInteger(height)
|
|
25
|
+
|| width<1 || height<1 || width*height>16777216 || !(bytes instanceof Uint8Array)
|
|
26
|
+
|| bytes.byteLength!==width*height*4) throw Error('Invalid raw frame');
|
|
27
|
+
const directory=this.directory;
|
|
28
|
+
let count=0,total=0;
|
|
29
|
+
for(const name of readdirSync(directory)){
|
|
30
|
+
const path=join(directory,name);
|
|
31
|
+
try{
|
|
32
|
+
const stat=statSync(path);
|
|
33
|
+
if(Date.now()-stat.mtimeMs>60000){unlinkSync(path);continue;}
|
|
34
|
+
count++;total+=stat.size;
|
|
35
|
+
}catch(error){if(error.code!=='ENOENT')throw error;}
|
|
36
|
+
}
|
|
37
|
+
if(count>=16||total+bytes.byteLength>128*1024*1024)throw Error('Raw frame consumer stalled');
|
|
38
|
+
const path=join(directory,`${++this.sequence}.${format}`);
|
|
39
|
+
writeFileSync(path,bytes,{flag:'wx',mode:0o600});
|
|
40
|
+
return JSON.stringify({version:1,transport:'file',path,width,height,stride:width*4,format,bytes:bytes.byteLength});
|
|
41
|
+
}
|
|
42
|
+
close(){
|
|
43
|
+
if(this.closed)return;this.closed=true;
|
|
44
|
+
process.removeListener('exit',this.onExit);
|
|
45
|
+
if(this.ownsDirectory&&this.path)rmSync(this.path,{recursive:true,force:true});
|
|
46
|
+
}
|
|
47
|
+
}
|