Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
317313a
[API] Add client file selection on IClientWindow
spaingnv Sep 30, 2026
fba1dc2
Ignore duplicate in-flight file-selection ids instead of aborting the…
spaingnv Sep 30, 2026
07a23a2
Fix CPU ruff isort and leftover review nits
spaingnv Sep 30, 2026
9ee2447
Harden client file selection against picker lifecycle and untrusted u…
spaingnv Sep 30, 2026
f99b69a
Apply ruff format to the file-selection WebRTC changes
spaingnv Oct 1, 2026
ecc349b
Merge origin/main into client file selection
spaingnv Oct 1, 2026
d0c5262
Let apps declare file-picker type and size; windows enforce the ceiling
spaingnv Oct 1, 2026
7eaa532
Report distinct file-picker outcomes and classify type before size
spaingnv Oct 1, 2026
4d9bc78
Complete failed native picks and drop stale WebRTC uploads on handoff.
spaingnv Oct 1, 2026
a158c5c
Fix CPU lint: file-selection request tuple type and ruff format.
spaingnv Oct 1, 2026
167e1c9
Merge origin/main into client file selection
spaingnv Oct 2, 2026
1f7f06a
Serve client file picks one at a time with a stable request_id per co…
spaingnv Oct 2, 2026
098e19f
Complete leftover file picks as unavailable when the client is gone.
spaingnv Oct 2, 2026
5469906
Drop in-flight file picks when the client is gone.
spaingnv Oct 2, 2026
3823bf3
Narrow selected-file test assertions so ty can type-check them.
spaingnv Oct 2, 2026
c4d55bf
Format webrtc_server.py to match the ruff-format hook.
spaingnv Oct 2, 2026
de0fdbf
Complete every file pick through the shared gate, including failed ch…
spaingnv Oct 2, 2026
c5fb189
Merge origin/main into client file selection
spaingnv Oct 2, 2026
9139b9a
Drop leftover native file picks when a replacement session reopens th…
spaingnv Oct 2, 2026
6502aa8
Drop duplicate file-selection tests that rechecked the same contract.
spaingnv Oct 2, 2026
b82b603
Call the WebRTC file prompt a request to select, not open.
spaingnv Oct 3, 2026
5e47b09
Keep request_id as the app-owned button id and let apps pick multiple…
spaingnv Oct 5, 2026
0f3e25b
Check upload type before size and keep newlines in zenity filenames.
spaingnv Oct 5, 2026
d4ebdbb
Decode multipart transfer encoding and unblock CPU lint.
spaingnv Oct 5, 2026
5da66c3
Merge origin/main into client file selection
spaingnv Oct 6, 2026
eda40c9
Expose client file selection as an every-tick IUILoop.file_selector
spaingnv Oct 6, 2026
a2676c5
Merge origin/main into client file selection
spaingnv Oct 6, 2026
12ad58b
Reject one request_id used by two file_selector controls in a tick
spaingnv Oct 12, 2026
7cd6260
Require every IClientWindow to answer file selection requests
spaingnv Oct 12, 2026
f40cac8
Add --disable-file-selection to turn off client file pickers for a run
spaingnv Oct 12, 2026
14ccd51
Drop test checks that only restate UI copy or dataclass fields
spaingnv Oct 12, 2026
bafa872
Merge remote-tracking branch 'origin/main' into dev/spaing/client-fil…
spaingnv Oct 12, 2026
0a2f8fd
Remove --disable-file-selection; the host decides whether a picker works
spaingnv Oct 12, 2026
320b68f
Default IClientWindow.request_selected_files to an immediate UNAVAILA…
spaingnv Oct 12, 2026
d9f201b
Clarify file selector request IDs in documentation
spaingnv Oct 12, 2026
1747aa1
Merge origin/main into dev/spaing/client-file-pick-649
spaingnv Oct 12, 2026
864a51f
Time out file pickers the host never shows
spaingnv Oct 13, 2026
a949fd5
Harden file selection against stale timers and lost results
spaingnv Oct 13, 2026
bf7c44a
Keep file results across disconnects and report types before batch size
spaingnv Oct 13, 2026
7616e5e
Shorten the file picker presentation deadline to 30 seconds
spaingnv Oct 13, 2026
f816dd7
Merge remote-tracking branch 'origin/main' into dev/spaing/client-fil…
spaingnv Oct 13, 2026
5352490
Keep filename spaces and keep prompt keys out of the app
spaingnv Oct 13, 2026
aed4a5f
Simplify file selection to one picker per session
spaingnv Oct 13, 2026
cf7ca1e
Merge remote-tracking branch 'origin/main' into dev/spaing/client-fil…
spaingnv Oct 13, 2026
cc5a619
Merge remote-tracking branch 'origin/main' into dev/spaing/client-fil…
spaingnv Oct 13, 2026
6a4171e
Keep _apply_window_requests unchanged and drop the per-tick request_i…
spaingnv Oct 13, 2026
6d098da
Drop the removed per-tick request_id check from the API README
spaingnv Oct 14, 2026
7ee3599
Document why request_id is app-supplied and must not be shared
spaingnv Oct 14, 2026
938cb4d
Move file selection rules into IClientWindow
spaingnv Oct 15, 2026
35f355d
Keep one file selection per UI tick
spaingnv Oct 15, 2026
290d81d
Drop stale file selector id comment
spaingnv Oct 15, 2026
be952ac
Harden file selection and document its contract
spaingnv Oct 15, 2026
4d0eded
Answer busy file selections and check results in IClientWindow
spaingnv Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/action2v/tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from numpy import uint64

from flashdreams.api_v2.client_window import IClientWindow
from flashdreams.api_v2.user_input_event import UserInputEvent
from flashdreams.runtime_v2.session_desc import SessionDesc
from flashdreams.runtime_v2.session_runner import run_session
from flashdreams.runtime_v2.step_result import StepResult
Expand Down Expand Up @@ -138,6 +139,9 @@ def __init__(self) -> None:
self.opened = False
self.cursor_requests: list[tuple[str, bool]] = []

def _put_user_input_event(self, event: UserInputEvent) -> None:
del event

def open(self, session_desc: SessionDesc) -> None:
del session_desc
self.opened = True
Expand Down
4 changes: 4 additions & 0 deletions apps/t2v/t2v/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import torch

from flashdreams.api_v2.client_window import IClientWindow
from flashdreams.api_v2.user_input_event import UserInputEvent
from flashdreams.runtime_v2.blit_model_output_to_screen_loop import (
BlitModelOutputToScreenLoop,
)
Expand Down Expand Up @@ -410,6 +411,9 @@ class _DiscardingClientWindow(IClientWindow):
def get_user_input_events(self) -> UserInputEvents:
return UserInputEvents([])

def _put_user_input_event(self, event: UserInputEvent) -> None:
del event

def open(self, session_desc: SessionDesc) -> None:
del session_desc

Expand Down
4 changes: 4 additions & 0 deletions apps/t2v/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from flashdreams.api_v2.client_window import IClientWindow
from flashdreams.api_v2.loop import IModelLoop
from flashdreams.api_v2.session import ISession
from flashdreams.api_v2.user_input_event import UserInputEvent
from flashdreams.runtime_v2 import cli
from flashdreams.runtime_v2.application_registry import (
APPLICATION_ENTRY_POINT_GROUP,
Expand Down Expand Up @@ -160,6 +161,9 @@ def __init__(self) -> None:
def get_user_input_events(self) -> UserInputEvents:
return UserInputEvents([])

def _put_user_input_event(self, event: UserInputEvent) -> None:
del event

def open(self, session_desc: SessionDesc) -> None:
self.session_desc = session_desc

Expand Down
64 changes: 64 additions & 0 deletions docs/source/documentation/demo_api/api_reference/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,70 @@ Request a positive downstream client-window width and height.
- `TypeError`: The size is not a pair of integers.
- `ValueError`: Either dimension is not positive.

#### `request_file_selector`

```python
def request_file_selector(self, request_id: str, initial_path: str | None=None, *, accept: Sequence[str]=(), max_file_bytes: int | None=None, multiple: bool=False) -> None
```

Ask the client to show a file selector. The call only queues the request; the
result arrives on a later tick as one `SelectedFilesUserInputEvent` in
`user_events`, carrying `request_id`. The model loop receives the same event.

Every call gets exactly one event unless its session is replaced or shut down
first. One selector is open at a time: a call made while one is open, including
a second call in the same tick, is answered `BUSY`. Use one stable
`request_id` per control.

Read `status` (`SelectedFilesStatus`) before `files`; `files` is empty unless
the status is `OK`:

- `OK`: each `SelectedFile` has a base `name` (no host path) and its `data`
bytes.
- `CANCELLED`: the user dismissed the selector.
- `BUSY`: another selector was open, so this one was not shown.
- `DISALLOWED_TYPE`: a file did not match `accept` (case-insensitive suffixes).
- `TOO_LARGE`: a file exceeded `max_file_bytes`, or the set exceeded
`MAX_SELECTED_FILE_BATCH_BYTES` (256 MiB) or `MAX_SELECTED_FILE_COUNT` (256).
- `UNAVAILABLE`: no picker (MP4, null), the picker was not shown within 30
seconds, the viewer left, the chooser failed, or a file could not be read.

`accept` checks filename suffixes only, so validate `data` as untrusted input.
`max_file_bytes` above `MAX_SELECTED_FILE_BYTES` (32 MiB) is clamped.
`initial_path` is a directory hint for the native Tk dialog and is ignored by
the browser.

```python
from flashdreams.runtime_v2.selected_file import SelectedFilesStatus
from flashdreams.runtime_v2.user_input_event import SelectedFilesUserInputEvent

if imgui.button("Open image"):
self.request_file_selector("open-image", accept=(".png",))
for event in self.user_events.get_events():
if not isinstance(event, SelectedFilesUserInputEvent):
continue
if event.request_id != "open-image":
continue
if event.status is SelectedFilesStatus.OK:
image_bytes = event.files[0].data
else:
self.state.message = f"No image: {event.status.value}"
```

**Parameters**

- `request_id`: Names the control that asked; echoed on the result.
- `initial_path`: Native directory hint; `None` uses the host user's home.
- `accept`: Filename suffixes such as `.png`. Empty allows any type.
- `max_file_bytes`: Maximum size of each file, or `None` for the ceiling.
- `multiple`: Whether the selector may return more than one file.

**Raises**

- `TypeError`: An argument has the wrong type.
- `ValueError`: `request_id` or `initial_path` is empty, or `accept` /
`max_file_bytes` is invalid.

#### `get_or_create_ui_loop_requests`

```python
Expand Down
9 changes: 8 additions & 1 deletion flashdreams/flashdreams/api_v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,14 @@ UI control that needs a fresh application session calls
`request_new_session(session_desc)` with a fully resolved replacement
description; the runtime cleans the current session and passes that description
to `ApplicationRunner` unchanged. Remaining timeout and remaining model steps
still apply to the replacement.
still apply to the replacement. To let the user pick a client file, call
`request_file_selector(request_id, ...)` when they ask (for ImGui, when
`imgui.button(...)` returns `True`), with one stable `request_id` per control.
The result arrives on a later tick as one `SelectedFilesUserInputEvent`
carrying that `request_id`; read its `status` before its `files`. One picker
is open at a time, and a call made while one is open is answered `BUSY`. The
`request_file_selector` docstring is the full contract: timing, every status,
size and type limits, and what WebRTC shows.

For SlangPy's smaller retained widget API, subclass `SlangPyUILoop` from
`flashdreams.runtime_v2.slangpy_ui_loop`. The
Expand Down
Loading