SY-4112: Add Schematic Symbol to Embed Media and iFrames#2260
SY-4112: Add Schematic Symbol to Embed Media and iFrames#2260
Conversation
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (78.09%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## rc #2260 +/- ##
==========================================
+ Coverage 64.22% 64.35% +0.13%
==========================================
Files 2157 2157
Lines 108983 109068 +85
Branches 8297 8310 +13
==========================================
+ Hits 69989 70186 +197
+ Misses 32983 32873 -110
+ Partials 6011 6009 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9fd1302 to
3c91cce
Compare
| .plugin(tauri_plugin_fs::init()) | ||
| .plugin(tauri_plugin_process::init()) | ||
| .setup(|app| { | ||
| // Create the main window manually so we can attach on_new_window handler | ||
| let main_window_config = app.config().app.windows.first().cloned() | ||
| .expect("No window config found"); | ||
| let app_handle_for_main = app.handle().clone(); | ||
| WebviewWindowBuilder::from_config(app.handle(), &main_window_config) | ||
| .expect("Failed to create main window builder") | ||
| .on_new_window(move |url, _features| { | ||
| #[allow(deprecated)] | ||
| let _ = tauri_plugin_shell::ShellExt::shell(&app_handle_for_main) | ||
| .open(url.to_string(), None::<tauri_plugin_shell::open::Program>); | ||
| NewWindowResponse::Deny | ||
| }) | ||
| .build() |
There was a problem hiding this comment.
expect() panics in setup closure instead of propagating errors
The three expect() calls inside the setup closure will panic (hard crash) if any of them fail, rather than returning an error. The closure signature is |app| -> Result<(), Box<dyn Error>>, so ? is the correct idiom here and is already used on the very next statement (app.handle().plugin(...)?). A failure to build the main window should surface as a clean error exit, not a panic.
pjdotson
left a comment
There was a problem hiding this comment.
Two things:
- I really don't think we should allow a general iframe embed. iframes could have potential security risks from users embedding untrusted content, all they are is just embedding a mini browser where it is likely that the user would just prefer the actual browser. It feels disconnected from our product, and I would really not want to add it unless we see a lot of evidence of user demand for it. Also, there is going to be a split between use cases for Synnax where it may primarily be used via a desktop app (like having an operator on-console during a test) and where it may primarily be used via browser (a factory setting where a lot of things are constantly running). I would be a little worried about users getting frustrated if a link that is supposed to open in a new tab doesn't work in an iframe for instance. I don't completely know what the right answer here is, but am curious to hear your thoughts. Personally, I think what might make more sense is an "image" and "video" element where you can link to an image or a video, and then we wrap that in the proper tag (we already have a
Videoelement in Pluto). - there is a lot of edited code in both the Drift and Rust code that I am not sure is necessary and also isn't tested / can cause odd issues. We should talk through this together.
| const { data, ...log } = await client.logs.retrieve({ key }); | ||
| placeLayout(Log.create({ ...anyStateZ.parse(data), ...log })); | ||
| const log = await client.logs.retrieve({ key }); | ||
| placeLayout(Log.create({ ...log.data, ...log })); |
There was a problem hiding this comment.
why aren't we parsing the log data?
There was a problem hiding this comment.
The other handlers don't parse either, and the spread of .data already extracts the state. I was just aiming for consistency with the other link.ts files. Should I change this?
| NewWindowResponse::Deny | ||
| }) | ||
| .build() | ||
| .expect("Failed to build main window"); |
There was a problem hiding this comment.
let's talk in person about why we have to do all of this stuff. I am not really
convinced it's necessary, especially since the documentation iframe already embeds fine
There was a problem hiding this comment.
Without this, right-clicking a link inside an iframe and selecting "Open in New Window/Tab" silently fails (no URL opens, regardless of protocol). Tauri's default webview denies new window requests when no on_new_window handler is set. The change attaches a handler that opens the link in the native browser, which then let's us open synnax:// urls that are embedded in the iframe page content.
What I tried:
allow-top-navigation-by-user-activationin the iframe sandbox. Doesn't fire for custom protocols likesynnax://- Tauri's on_navigation handler. Fires for http/https but not custom protocols from sandboxed iframes
registerProtocolHandlerBrowsers only support web+ prefix
Another thing I tried to do was to "intercept" clicks on a synnax:// hyperlink to allow users to seamlessly open the page links. Since they are not valid links, when clicked from within an iframe, no navigation occurs. But I couldn't get this to work.
| keys: Object.keys(filteredData[0]), | ||
| keys: [...keys], | ||
| threshold: 0.3, | ||
| ignoreLocation: true, |
There was a problem hiding this comment.
what does ignoreLocation do?
There was a problem hiding this comment.
It tells Fuse.js to score matches equally regardless of where they appear in the string. For MediaEmbed, I wanted various searches to work:
"Media Embed, Picture Embed, Video Embed.... PNG Embed...."
The problem was that PNG Embed was so deep into the string, that searching for "PNG Embed" returned 0 results instead of finding the MediaEmbed symbol.
| }: SymbolProps<MediaEmbedProps>): ReactElement => ( | ||
| <Primitives.Embed dimensions={dims} color={colorVal} placeholder="Enter a URL"> | ||
| {url != null && url.length > 0 ? ( | ||
| <img src={url} style={{ width: "100%", height: "100%", objectFit: "contain" }} /> |
There was a problem hiding this comment.
should this always be wrapped in a img tag? why not use a video tag for videos and
live streams?
There was a problem hiding this comment.
Using a video tag prevents us from rendering PNGs, Gifs, SVGs, or other image formats. video is needed for HLS/DASH/MP4, which I don't see a use case for now. Maybe in the future when we have proper video stream handling.
|
…Image Dimensions, Remove Embed Border
…tainer-schematic-symbol
35f3551 to
2a56428
Compare
Issue Pull Request
Linear Issue
SY-4112
Description
MediaEmbedsupports mjpeg, svg, png, and gifsIframeEmbedsupports iframes (e.g. Grafana dashboards) with asandboxattribute for security. A "Block Cookies" toggle controls whether the
embedded site can use cookies (
allow-same-origin).PageEmbedto later support embedding Synnax logs, plots, andtables. Proof of concept was shown for page embeds, but that feature will
require a larger refactor and is outside the scope of this feature.
useStaticDatasearch improvements: Fuse.js keys are now collected from allitems (not just the first), and
ignoreLocationenables substring matchinganywhere in a field.
.
Basic Readiness
Greptile Summary
This PR adds
MediaEmbed(mjpeg/svg/png/gif) andIframeEmbed(iframe with sandbox) schematic symbols, edge/corner resize handles viaNodeResizeControlin the Grid layer, and improvesuseStaticDataFuse.js search to collect keys from all items and enableignoreLocation. The Tauri window creation is refactored to use aninvoke-based command so theon_new_windowhandler can intercept and redirect navigations for new windows.console/src/log/services/link.ts(re-introduceddatakey in spread and droppedanyStateZ.parse()validation) remains unaddressed.expect()panics in the Taurisetupclosure (main.rs) flagged in the prior round are also still present.Confidence Score: 3/5
Not safe to merge until the outstanding P1 in link.ts (data key re-introduced, schema validation removed) is resolved.
Score reduced from the P1 ceiling of 4 because the link.ts defect is a pre-existing flagged issue that is still unaddressed, and the main.rs expect() panics remain. The new embed/resize/search code itself is clean and well-tested.
console/src/log/services/link.ts (P1 data-key / schema-validation regression), console/src-tauri/src/main.rs (expect() panics in setup closure)
Important Files Changed
datakey into spread and removesanyStateZ.parse()validation — P1 issue flagged in prior review, not yet addressed.create_webview_windowTauri command withon_new_windowhandler; setup closure usesexpect()(flagged in prior review) and manually creates the main window with"create": falsein config.buildWindowOptionshelper and replaces WebviewWindow instantiation withinvoke("create_webview_window"), correctly preserving position-visibility clamping before the call.onResizeprop and renders NodeResizeControl handles (edges + corners) when editable; exportsroundResizeDimshelper.ignoreLocationfor substring matching anywhere in a field..pluto-symbol-primitive-scoped to any selected node — intentional to support new embed symbols that lack the primitive class.Comments Outside Diff (3)
pluto/src/schematic/symbol/Symbols.tsx, line 901-908 (link)allow-scripts+allow-same-originsandbox escapeWhen both
allow-scriptsandallow-same-originare set together on an iframe sandbox, a script running inside the iframe — if it is same-origin with the Synnax app — can callwindow.frameElement.removeAttribute("sandbox")and fully escape the sandbox. MDN explicitly warns against this combination for untrusted content. For cross-origin embeds (e.g. Grafana on a different host)window.frameElementreturnsnull, so the risk doesn't materialise in the common case, but it's worth adding a code comment here to document the known limitation alongside the existing postMessage warning.console/src/log/services/link.ts, line 92-93 (link)datakey leaked into spread and schema validation removedThe old code destructured
dataout of the retrieve result so thatlogdid not contain adatakey, then validated it withanyStateZ.parse(data). The new code spreads...logdirectly, which re-introducesdata: log.datainto the object passed toLog.create. This meansLog.createnow receives both the individual flattened fields fromlog.dataand a top-leveldatakey pointing at the same nested object — a different shape than before. The silent removal ofanyStateZ.parse()also drops the schema validation that previously guaranteed the data conformed to the expected shape before layout creation.console/src/log/services/link.ts, line 3-5 (link)datakey re-introduced and schema validation removed{ ...log.data, ...log }spreadslog.data's fields first, then overwrites withlog's top-level fields — which include adatakey pointing at the same nested object.Log.createnow receives an unexpecteddataproperty that the old code explicitly stripped out via destructuring. The old code also validated throughanyStateZ.parse(data)before use; that guard is gone, so a malformed server response will propagate silently instead of throwing at the boundary.Reviews (8): Last reviewed commit: "Merge remote-tracking branch 'origin/rc'..." | Re-trigger Greptile