-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: snapshots #8710
feat: snapshots #8710
Conversation
🦋 Changeset detectedLatest commit: b4a6f25 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
} | ||
const scroll_positions = storage.get(SCROLL_KEY) ?? {}; | ||
|
||
/** @type {Record<string, any[]>} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** @type {Record<string, any[]>} */ | |
/** @type {Record<number, any[]>} */ |
I see initial_history_index
and current_history_index
which both seem to be number
s to be used as an index for snapshots
.
Types: since it's the same interface everywhere, we could export it from import type { PageData, ActionData } from './$types';
import type { Snapshot } from '@sveltejs/kit';
export let data: PageData;
export let form: ActionData;
export const snapshot: Snapshot = {
capture: () => 'hello',
restore: (message) => console.log(message)
}; But if feels a tiny bit awkward compared to having all the types come from the same place: import type { PageData, ActionData, Snapshot } from './$types';
export let data: PageData;
export let form: ActionData;
export const snapshot: Snapshot = {
capture: () => 'hello',
restore: (message) => console.log(message)
}; Also, unless I'm mistaken there's no way to automatically infer export interface Snapshot<T = any> {
capture: () => T;
restore: (snapshot: T) => void
} ...whereas if it's a generated type then we could in theory do something clever and misguided with proxy types so that |
It would probably work with a proxy type, but only with a proxy type, which is unfortunate because with If we do add it to |
I was imagining something like this, which I think would work non-breakingly? export type Snapshot<T = ReturnType<typeof import('./proxy+page.svelte').snapshot.capture>> = Kit.Snapshot<T>; It's a shame that it can't be inferred properly, generics are such a bullshit solution to this problem. (Or can it? Am I missing a trick?) |
Yes, done that way (generic with a default value) it would work in a backwards-compatible way. We would need some way to say "infer this second property from the first", but I don't think there's a way to do that. It's available for functions ( |
probably, though |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good! But I think there's a memory leak here. That index
is never deleted, snapshots
could grow larger and larger over time. At the same time I'm not sure how that could be prevented, since you could navigate around for quite a while, then go back to the start and expect that snapshot to still be visible. And what happens if you navigate back and forth (not using links but browser back/fwd)? We can't really tell when it's safe to delete it. Do we need some kind of config there? export const snapshots = { ... , deleteAfter: 10 }
with a default of X?
There is a memory leak, but it would be almost impossible for it to have an impact — you'd have to have a session with hundreds if not thousands of history entries before it was more than a rounding error, unless you were snapshotting absurdly large objects. It's a memory leak in the same way that scroll restoration causes a memory leak. So my inclination is probably to handwave it away, though we could mitigate it by not storing anything in the case where nothing was snapshotted |
I imagine some internal business app open for a week straight - it could have an impact then. We should at least note something about it in the docs. |
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
|
I figured documentation is sufficient for now, though I did realise that we can at least prune stuff in the case where you navigate back then push a new state. |
Part of #5478. This adds the ability to capture DOM state immediately before transitioning away from a page, and restoring it if the user traverses back to that page (think bfcache, but with more control).
Any
+layout.svelte
or+page.svelte
component can export asnapshot
object:(The initial proposal was
apply
, but Copilot suggestedrestore
and I prefer it.)Things to note:
<Foo>
is the 'same' as that instance of<Foo>
the last time the document was rendered), but we can reliably do this for route filessessionStorage
, so it must be JSON-serializablesessionStorage
, state persists across documents, and survives reloadsIn order to read theactually this isn't true,snapshot
object, we have to set theaccessors: true
compile option on+layout.svelte
and+page.svelte
files, courtesy ofexperimental.dynamicCompileOptions
. This will have an impact on bundle size, but a very modest oneexport const
creates accessors automatically. neat!TODO
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm test
and lint the project withpnpm lint
andpnpm check
Changesets
pnpm changeset
and following the prompts. Changesets that add features should beminor
and those that fix bugs should bepatch
. Please prefix changeset messages withfeat:
,fix:
, orchore:
.