Skip to content

Conversation

@texodus
Copy link
Member

@texodus texodus commented Jan 21, 2026

This PR updates the <perspective-viewer> and <perspective-workspace> APIs to follow a common pattern for binding to a Perspective Server, with the goal of unifying these components in the future.

Screenshot 2026-01-20 at 10 49 56 PM

New <perspective-workspace> theme support

Until now, <perspective-viewer>'s load() method has taken a Table proxy object as an argument. This has a few disadvantages:

  • Because load and restore both render, calling both methods in sequence (as is often practically the case) involves convoluted internal state management to make sure renders are conserved and a single-frame un-restored UI is not drawn, and the calls must be made in the right order which is a poor DX for frameworks like React.
  • Loading a new Table in an existing perspective-viewer requires an internal config reset and a superfluous intermediate View if followed by a restore.
  • Multi-Table features (like joins) can't be supported.

Meanwhile, <perspective-workspace> has a reactive Map of Table proxy objects with names. However, this API very closely mirrors what already existed in Client, which is already a sort-of collection of Tables, and even has an existing name field already (albeit a different one than what <perspective-workspace> previously supported):

const table = await worker.table(data, {name: "data_soure"});

Both of these APIs have been replaced (or in <perspective-viewer> case, supplanted) with a new load() method which takes a Client and binds the widget to this Client exclusively. To support this, the save() and restore() methods on <perspective-viewer> now take a table field in their ViewConfig, which names the table.

With this change, calling load() with a Client argument first on a <perspective-viewer> does not cause a render (because no Table is selected yet), and restore() selects both the Table and its ViewConfig, guaranteeing one atomic render once both are called and the element is mounted.

Calling load() with a Table is now internally the equivalent of this, which emulates legacy support:

await viewer.load(await table.get_client());
await viewer.restore({table: await table.get_name()});

While this works, it should be avoided in favor of using a Client to prevent extraneous renders.

Upgrade Notice

There is one quirky footgun to be aware of with the new unified API. Until now, name has been an optional keyword argument to the Table constructor, and omitting it internal led to an invisble random name assignment. However, relying on this behavior will now lead to non-symmetric behavior if you do not carefully retain this mapping in the name field of ViewConfig.

For example:

// (1) `name` omitted, `tables` has a random name `foo`.
const table = await worker.table(data);
viewer.load(worker);

// ... later ...

// `ViewConfig` saved here has name `foo`
localStorage.setItem("layout", JSON.stringify(await viewer.save()));

// ... User refreshes the browser here

// Same code as (1) above, `name` omitted, but `table` 
// has a random name `bar` now.
const table = await worker.table(data);

// Does not work!
viewer.restore(JSON.parse(localStorage.getItem("layout")));

You can avoid this by (painfully) syncing the table fields manually, or by simply always giving your Table a name on construction.

Summary

<perspective-viewer> changes:

  • load() now takes a Client as an argument, or (optionally) a legacy Table.
  • restore() now takes a ViewConfig with a table field, corresponding to the name of the Perspective Table.
  • save() now returns a ViewConfig with this field.

<perspective-workspace> changes:

  • addTable(), removeTables() methods and the tables property have been removed.
  • load() method now takes a Client, like <perspective-viewer>.
  • UX support for per-viewer themes.

Drive-by changes:

  • Fix abseil build issue on macos-15.
  • UX cleanup.
  • Duplicate Table names in the engine now error (instead of silently overwriting eachother).

@texodus texodus added enhancement Feature requests or improvements breaking labels Jan 21, 2026
Signed-off-by: Andrew Stein <steinlink@gmail.com>
@texodus texodus marked this pull request as ready for review January 21, 2026 04:36
…` to take a `Client` instead of a `Table`

Signed-off-by: Andrew Stein <steinlink@gmail.com>
Signed-off-by: Andrew Stein <steinlink@gmail.com>
@texodus texodus merged commit 390b7ea into master Jan 21, 2026
16 checks passed
@texodus texodus deleted the viewer-client branch January 21, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking enhancement Feature requests or improvements

Development

Successfully merging this pull request may close these issues.

2 participants