@svgrid/enterprise 3.1.4 → 3.1.5
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/dist/cdn/svgrid-enterprise.svelte-external.js +8956 -8606
- package/dist/designer/assets/GridMenus-CvcASlKv.js +8 -0
- package/dist/designer/assets/SvListBox-BlmmzYcU.js +110 -0
- package/dist/designer/assets/{index-BgVTSVoK.js → index-B3WBezq_.js} +432 -370
- package/dist/designer/assets/index-KmSEY7Lb.css +1 -0
- package/dist/designer/assets/src-D4PRZJYA.js +58 -0
- package/dist/designer/assets/{validate-YxSrBF1H.js → validate-B71dE1Cs.js} +1 -1
- package/dist/designer/index.html +4 -4
- package/dist/node/studio.js +402 -81
- package/dist/server/server-row-model.js +199 -26
- package/dist/studio/emit-project.js +86 -56
- package/dist/studio/emit-schema.d.ts +3 -0
- package/dist/studio/emit-schema.js +128 -29
- package/dist/studio/index.d.ts +2 -1
- package/dist/studio/index.js +2 -1
- package/dist/studio/init-flow.d.ts +3 -0
- package/dist/studio/init-flow.js +5 -2
- package/dist/studio/project.d.ts +10 -1
- package/dist/studio/project.js +28 -15
- package/dist/studio/samples/restaurant.js +1 -1
- package/dist/studio/sveltekit3.d.ts +38 -0
- package/dist/studio/sveltekit3.js +194 -0
- package/dist/studio/ui-components.generated.js +15 -4
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/watermark.js +31 -1
- package/dist/wc/{GridMenus-6_fF5jIm.js → GridMenus-fWbBUiRH.js} +121 -121
- package/dist/wc/sv-sheet-element.js +10979 -10522
- package/dist/wc/{svelte-virtualizer.svelte-Du4tjrIP.js → svelte-virtualizer.svelte-Dntdh8Ms.js} +103 -56
- package/dist/wc/{validate-JAuMsk6Z.js → validate-43C03DLP.js} +1 -1
- package/package.json +3 -3
- package/dist/designer/assets/GridMenus-DGZJKFvX.js +0 -8
- package/dist/designer/assets/SvListBox-v2jm0-vf.js +0 -110
- package/dist/designer/assets/index-l21iGkpF.css +0 -1
- package/dist/designer/assets/src-BqEXoOog.js +0 -58
package/dist/studio/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { introspectDatabase, listDatabaseTables, countTableRows, probeConnection
|
|
|
13
13
|
export { buildConnectionString, parseConnectionString, redactConnectionString, isFileDialect, DRIVER_PACKAGE, DEFAULT_PORT, } from './db-connect-string.js';
|
|
14
14
|
export { scaffold, mergeManaged, skipUserOwned, MANAGED_START, MANAGED_END, } from './scaffold.js';
|
|
15
15
|
export { scaffoldApp } from './scaffold-app.js';
|
|
16
|
+
export { toSvelteKit3, SVELTEKIT_3_DEV_DEPENDENCIES, SVELTEKIT_3_ADAPTERS, } from './sveltekit3.js';
|
|
16
17
|
export { emitStudioApp, emitEntityModules, entityScreenPage, prepareEntities } from './emit-schema.js';
|
|
17
18
|
export { emitStudioProject, emitStudioAppBundle, emitStudioFragment, runtimeDeps, studioDeployInfo, screenHandles, screenDataset, ctxCompletions, componentHandleMembers, ctxAmbientDts, GRID_API_MEMBERS } from './emit-project.js';
|
|
18
19
|
export { sampleApps, getSampleApp } from './samples/index.js';
|
|
@@ -28,7 +29,7 @@ export { studioThemes, defaultStudioTheme, getStudioTheme, resolveThemeTokens, r
|
|
|
28
29
|
export { createProject, defaultScreenFor, defaultBlockConfig, gridColumns, entityOf, blockPalette, addBlock, addBlockAt, addComponentBlock, removeBlock, duplicateBlock, moveBlock, reorderBlock, updateBlock, addEntity, removeEntity, updateEntity,
|
|
29
30
|
// Form layout: the operations a form builder drives, on the entity's own
|
|
30
31
|
// `form` so a built form travels with the entity rather than with one block.
|
|
31
|
-
formPlan, setEntityForm, setFormColumns, addFormSection, updateFormSection, removeFormSection, moveFormSection, moveFormField, moveFormFields, setFieldConditions, updateEntityField, setFieldInput, setFieldHidden, formControlsFor, formControlSettings, suggestFormSections, addScreen, addFreestandingScreen, addScreenAction, removeScreenAction, enableScreenCode, disableScreenCode, setScreenRenderGrid, screenLayoutOf, buildDockLayout, reconcileDock, syncDockPanes, setScreenLayout, isPaneLayout, PANE_LAYOUTS, buildCanvasLayout, canvasRectOf, setCanvasRect, CANVAS_COLS, CANVAS_ROW_PX, CANVAS_GAP_PX, applyGridPreset, gridPresetLabel, GRID_PRESETS, setScreenDock, reseedScreenDock, setDockPaneTitle, dockPaneTitleOf, dockPaneIds, setLayoutOpts, gridOpts, stackOpts, splitOpts, dockOpts, canvasOpts, setHandlerBody, setScreenHandlersSource, setHandlerSteps, addHandlerStep, updateHandlerStep, moveHandlerStep, stepsToCode, compileStep, compileHandlerSteps, compileValue, compileCondition, compileTriggerStep, compileTriggerSteps, triggersOf, setTrigger, TRIGGER_EVENTS, setJob, setTenancy, tenantField, isTenantScoped, clickSlot, ssrScreenShape, ssrEligible, withSsrDefaults, setScreenRenderMode, eventSlot, rowSelectSlot, changeSlot, FORM_SUBMIT, GRID_EVENTS, addStateVar, updateStateVar, removeStateVar, stateInitExpr, stateTsType, setComponentName, setComponentBinding, componentHasBindings, componentHandleName, ON_LOAD, removeScreen, updateScreen, duplicateScreen, reorderScreen, insertBlock, setDataSource, setDeployTarget, setAuth, setDataLayer, seedUsers, defaultEntitySource, flattenBlocks, TAB_CHILD_KINDS, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, addTab, removeTab, renameTab, addTabBlock, addTabComponent, removeTabBlock, addAccordionSection, removeAccordionSection, renameAccordionSection, setAccordionMultiple, addAccordionBlock, addAccordionComponent, removeAccordionBlock, setEntityDataSource, entityDataSource, setTheme, setThemePreset, setShell, sanitizeProject, screenFromTemplate, addScreenFromTemplate, serializeProject, parseProject, validateProject, isProjectValid, blockColumns, blockStyleCss, blockClassName, sanitizeClassName, mergeBlockStyle, roleCanScreen, roleCanAction, CRUD_ACTIONS, } from './project.js';
|
|
32
|
+
formPlan, setEntityForm, setFormColumns, addFormSection, updateFormSection, removeFormSection, moveFormSection, moveFormField, moveFormFields, setFieldConditions, updateEntityField, setFieldInput, setFieldHidden, formControlsFor, formControlSettings, suggestFormSections, addScreen, addFreestandingScreen, addScreenAction, removeScreenAction, enableScreenCode, disableScreenCode, setScreenRenderGrid, screenLayoutOf, buildDockLayout, reconcileDock, syncDockPanes, setScreenLayout, isPaneLayout, PANE_LAYOUTS, buildCanvasLayout, canvasRectOf, setCanvasRect, CANVAS_COLS, CANVAS_ROW_PX, CANVAS_GAP_PX, applyGridPreset, gridPresetLabel, GRID_PRESETS, setScreenDock, reseedScreenDock, setDockPaneTitle, dockPaneTitleOf, dockPaneIds, setLayoutOpts, gridOpts, stackOpts, splitOpts, dockOpts, canvasOpts, setHandlerBody, setScreenHandlersSource, setHandlerSteps, addHandlerStep, updateHandlerStep, moveHandlerStep, stepsToCode, compileStep, compileHandlerSteps, compileValue, compileCondition, compileTriggerStep, compileTriggerSteps, triggersOf, setTrigger, TRIGGER_EVENTS, setJob, setTenancy, tenantField, isTenantScoped, clickSlot, ssrScreenShape, ssrEligible, withSsrDefaults, setScreenRenderMode, eventSlot, rowSelectSlot, changeSlot, FORM_SUBMIT, GRID_EVENTS, addStateVar, updateStateVar, removeStateVar, stateInitExpr, stateTsType, setComponentName, setComponentBinding, componentHasBindings, componentHandleName, ON_LOAD, removeScreen, updateScreen, duplicateScreen, reorderScreen, insertBlock, setDataSource, setDeployTarget, setKitVersion, setAuth, setDataLayer, seedUsers, defaultEntitySource, flattenBlocks, TAB_CHILD_KINDS, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, addTab, removeTab, renameTab, addTabBlock, addTabComponent, removeTabBlock, addAccordionSection, removeAccordionSection, renameAccordionSection, setAccordionMultiple, addAccordionBlock, addAccordionComponent, removeAccordionBlock, setEntityDataSource, entityDataSource, setTheme, setThemePreset, setShell, sanitizeProject, screenFromTemplate, addScreenFromTemplate, serializeProject, parseProject, validateProject, isProjectValid, blockColumns, blockStyleCss, blockClassName, sanitizeClassName, mergeBlockStyle, roleCanScreen, roleCanAction, CRUD_ACTIONS, } from './project.js';
|
|
32
33
|
export { verifyScaffold, summarizeVerify, } from './verify.js';
|
|
33
34
|
export { UI_COMPONENT_REGISTRY, uiComponentSpec, gridPropSurface, gridApiSettableProps, gridIconGroups, GRID_CURATED_PROPS, STANDARD_UI_EVENTS, } from './ui-components.js';
|
|
34
35
|
export { buildCopilotMessages, projectFromModelText } from './copilot-core.js';
|
|
@@ -40,6 +40,9 @@ export type InitFlags = {
|
|
|
40
40
|
/** Theme preset id. */
|
|
41
41
|
theme?: string;
|
|
42
42
|
dark?: boolean;
|
|
43
|
+
/** SvelteKit major to target. 2 (the default) boots in StackBlitz; 3 needs
|
|
44
|
+
* Vite 8, which does not run there yet. Saved into studio.config.json. */
|
|
45
|
+
kit?: 2 | 3;
|
|
43
46
|
/** App title (skips the question). */
|
|
44
47
|
title?: string;
|
|
45
48
|
/** Take every default; ask nothing that has one. */
|
package/dist/studio/init-flow.js
CHANGED
|
@@ -10,7 +10,7 @@ import { emitStudioAppBundle } from './emit-project.js';
|
|
|
10
10
|
import { mergeManaged, skipUserOwned } from './scaffold.js';
|
|
11
11
|
import { getStudioTheme, studioThemes } from './themes.js';
|
|
12
12
|
import { UserError } from './user-error.js';
|
|
13
|
-
import { serializeProject } from './project.js';
|
|
13
|
+
import { serializeProject, setKitVersion } from './project.js';
|
|
14
14
|
const SOURCES = [
|
|
15
15
|
{ key: 'sample', label: 'Sample data', hint: 'a seeded pair of related tables - see a working app immediately' },
|
|
16
16
|
{ key: 'db', label: 'An existing database', hint: 'Postgres, MySQL, SQL Server or SQLite - reads your tables' },
|
|
@@ -465,7 +465,7 @@ export async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
465
465
|
const title = flags.yes || flags.title ? defaultTitle : (await prompts.ask('\nApp name:', defaultTitle)).trim() || defaultTitle;
|
|
466
466
|
const outDir = flags.out ?? (flags.yes ? '.' : (await prompts.ask('Write it where?', '.')).trim() || '.');
|
|
467
467
|
await confirmTargetFolder(outDir, flags, prompts, io, outDir === candidateDir ? markersBefore : null);
|
|
468
|
-
const
|
|
468
|
+
const built = crudAppFromSchemas(data.schemas, {
|
|
469
469
|
title,
|
|
470
470
|
dataSource: data.kind,
|
|
471
471
|
sources: data.sources,
|
|
@@ -474,6 +474,9 @@ export async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
474
474
|
overviewDashboard: true,
|
|
475
475
|
...(theme ? { theme } : {}),
|
|
476
476
|
});
|
|
477
|
+
// On the model, not only on this emit: studio.config.json is written from the
|
|
478
|
+
// same object below, so the designer reopens it as the same SvelteKit major.
|
|
479
|
+
const project = flags.kit === 3 ? setKitVersion(built, 3) : built;
|
|
477
480
|
const files = emitStudioAppBundle(project);
|
|
478
481
|
const written = await writeAll(files, outDir, io);
|
|
479
482
|
// The design itself, so `svgrid-studio designer` reopens exactly this app.
|
package/dist/studio/project.d.ts
CHANGED
|
@@ -1003,6 +1003,11 @@ export type StudioProject = {
|
|
|
1003
1003
|
/** Deploy target: picks the SvelteKit adapter + provider config the bundle emits.
|
|
1004
1004
|
* Defaults to `auto` (@sveltejs/adapter-auto, which detects Vercel/Netlify/Cloudflare). */
|
|
1005
1005
|
deploy?: DeployTarget;
|
|
1006
|
+
/** SvelteKit major the app targets. Absent means 2, the default: SvelteKit 3
|
|
1007
|
+
* needs Vite 8, whose Rolldown bundler does not run in StackBlitz's
|
|
1008
|
+
* WebContainer, so opting in trades that for v3. Saved in studio.config.json
|
|
1009
|
+
* so a v3 project is still v3 when it is reopened and regenerated. */
|
|
1010
|
+
kit?: 3;
|
|
1006
1011
|
/** Server-side business-rule triggers, keyed by entity name. Enforced on the
|
|
1007
1012
|
* SQL route (compiled into createKitHandlers `hooks`). */
|
|
1008
1013
|
triggers?: Record<string, EntityTriggers>;
|
|
@@ -1076,7 +1081,8 @@ export declare function screenRenderMode(_project: StudioProject, screen: Screen
|
|
|
1076
1081
|
export declare function ssrScreenShape(project: StudioProject, screen: Screen): 'grid' | 'read' | null;
|
|
1077
1082
|
/** Whether a screen can be emitted as SSR-native (any supported shape). */
|
|
1078
1083
|
export declare function ssrEligible(project: StudioProject, screen: Screen): boolean;
|
|
1079
|
-
/** Set a screen's render mode
|
|
1084
|
+
/** Set a screen's render mode. Both modes are stored: a screen switched to
|
|
1085
|
+
* 'spa' stays client-rendered even where `withSsrDefaults` would turn it on. */
|
|
1080
1086
|
export declare function setScreenRenderMode(project: StudioProject, screenId: string, mode: 'ssr' | 'spa'): StudioProject;
|
|
1081
1087
|
/** True when the screen should actually emit as SSR (mode is 'ssr' AND eligible). */
|
|
1082
1088
|
export declare function isSsrScreen(project: StudioProject, screen: Screen): boolean;
|
|
@@ -1402,6 +1408,9 @@ export declare function duplicateScreen(project: StudioProject, screenId: string
|
|
|
1402
1408
|
export declare function reorderScreen(project: StudioProject, screenId: string, toIndex: number): StudioProject;
|
|
1403
1409
|
export declare function setDataSource(project: StudioProject, dataSource: DataSourceKind): StudioProject;
|
|
1404
1410
|
/** Set the deploy target (SvelteKit adapter + provider config the bundle emits). */
|
|
1411
|
+
/** Target a SvelteKit major. 2 is the default and is stored as the field's
|
|
1412
|
+
* absence, the same way `deploy: 'auto'` is. */
|
|
1413
|
+
export declare function setKitVersion(project: StudioProject, kit: 2 | 3): StudioProject;
|
|
1405
1414
|
export declare function setDeployTarget(project: StudioProject, deploy: DeployTarget): StudioProject;
|
|
1406
1415
|
/** Enable / disable the authentication starter (login + session + hooks). */
|
|
1407
1416
|
export declare function setAuth(project: StudioProject, patch: Partial<AuthConfig> & {
|
package/dist/studio/project.js
CHANGED
|
@@ -302,8 +302,8 @@ export function ssrScreenShape(project, screen) {
|
|
|
302
302
|
return null;
|
|
303
303
|
const source = project.dataSources?.[screen.entity];
|
|
304
304
|
const kind = source?.kind ?? project.dataSource;
|
|
305
|
-
// memory
|
|
306
|
-
//
|
|
305
|
+
// memory and sql both reuse the entity's /api route via event.fetch (memory's
|
|
306
|
+
// rows are held on the server behind it); rest calls the remote API straight from the server, which needs
|
|
307
307
|
// an absolute URL to resolve there. (supabase and pglite stay SPA: pglite only
|
|
308
308
|
// exists in the browser, and a Supabase read carries the signed-in user's token,
|
|
309
309
|
// which a server-side call with the anon key would silently drop.)
|
|
@@ -334,11 +334,12 @@ export function ssrScreenShape(project, screen) {
|
|
|
334
334
|
export function ssrEligible(project, screen) {
|
|
335
335
|
return ssrScreenShape(project, screen) !== null;
|
|
336
336
|
}
|
|
337
|
-
/** Set a screen's render mode
|
|
337
|
+
/** Set a screen's render mode. Both modes are stored: a screen switched to
|
|
338
|
+
* 'spa' stays client-rendered even where `withSsrDefaults` would turn it on. */
|
|
338
339
|
export function setScreenRenderMode(project, screenId, mode) {
|
|
339
340
|
return {
|
|
340
341
|
...project,
|
|
341
|
-
screens: project.screens.map((s) => (s.id === screenId ? { ...s, renderMode: mode
|
|
342
|
+
screens: project.screens.map((s) => (s.id === screenId ? { ...s, renderMode: mode } : s)),
|
|
342
343
|
};
|
|
343
344
|
}
|
|
344
345
|
/** True when the screen should actually emit as SSR (mode is 'ssr' AND eligible). */
|
|
@@ -346,19 +347,19 @@ export function isSsrScreen(project, screen) {
|
|
|
346
347
|
return screenRenderMode(project, screen) === 'ssr' && ssrEligible(project, screen);
|
|
347
348
|
}
|
|
348
349
|
/**
|
|
349
|
-
* Turn server rendering on for every screen that can support it
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* overrides a choice somebody made.
|
|
350
|
+
* Turn server rendering on for every screen that can support it. Applied when a
|
|
351
|
+
* project is first generated, so a new app reads as idiomatic SvelteKit (a
|
|
352
|
+
* `load` and form `actions`) rather than a client SPA. Screens that already
|
|
353
|
+
* state a mode are left alone, so this never overrides a choice somebody made.
|
|
354
354
|
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
355
|
+
* Limited to sources that hold one copy of the data. SQL and REST always did:
|
|
356
|
+
* every path goes to the same database or the same remote API. In-memory rows
|
|
357
|
+
* are held once on the server ($lib/server/store) and every screen reaches them
|
|
358
|
+
* through the entity's /api route, so an SSR screen and an SPA screen see the
|
|
359
|
+
* same rows. PGlite lives in the browser's IndexedDB, which the server cannot
|
|
360
|
+
* read, so it stays client-rendered.
|
|
360
361
|
*/
|
|
361
|
-
const SSR_DEFAULT_KINDS = new Set(['sql', 'rest']);
|
|
362
|
+
const SSR_DEFAULT_KINDS = new Set(['sql', 'rest', 'memory']);
|
|
362
363
|
export function withSsrDefaults(project) {
|
|
363
364
|
return {
|
|
364
365
|
...project,
|
|
@@ -1805,6 +1806,15 @@ export function setDataSource(project, dataSource) {
|
|
|
1805
1806
|
return { ...project, dataSource };
|
|
1806
1807
|
}
|
|
1807
1808
|
/** Set the deploy target (SvelteKit adapter + provider config the bundle emits). */
|
|
1809
|
+
/** Target a SvelteKit major. 2 is the default and is stored as the field's
|
|
1810
|
+
* absence, the same way `deploy: 'auto'` is. */
|
|
1811
|
+
export function setKitVersion(project, kit) {
|
|
1812
|
+
if (kit === 2) {
|
|
1813
|
+
const { kit: _drop, ...rest } = project;
|
|
1814
|
+
return rest;
|
|
1815
|
+
}
|
|
1816
|
+
return { ...project, kit: 3 };
|
|
1817
|
+
}
|
|
1808
1818
|
export function setDeployTarget(project, deploy) {
|
|
1809
1819
|
if (deploy === 'auto') {
|
|
1810
1820
|
const { deploy: _drop, ...rest } = project;
|
|
@@ -2034,6 +2044,9 @@ export function parseProject(json) {
|
|
|
2034
2044
|
...(typeof p.audit === 'boolean' ? { audit: p.audit } : {}),
|
|
2035
2045
|
...(p.i18n && typeof p.i18n === 'object' ? { i18n: p.i18n } : {}),
|
|
2036
2046
|
...(typeof p.deploy === 'string' && p.deploy !== 'auto' ? { deploy: p.deploy } : {}),
|
|
2047
|
+
// Without this a SvelteKit 3 project reopened in the designer would quietly
|
|
2048
|
+
// regenerate as SvelteKit 2: parseProject keeps only the fields it lists.
|
|
2049
|
+
...(p.kit === 3 ? { kit: 3 } : {}),
|
|
2037
2050
|
...(p.triggers && typeof p.triggers === 'object' ? { triggers: p.triggers } : {}),
|
|
2038
2051
|
});
|
|
2039
2052
|
}
|
|
@@ -166,7 +166,7 @@ export const restaurant = {
|
|
|
166
166
|
]),
|
|
167
167
|
// Resource scheduler: a column per table, reservations placed by time + tinted by
|
|
168
168
|
// status, drag-to-reassign a table + a detail drawer (day view). Was a month calendar.
|
|
169
|
-
schedulerScreen(orders, { id: 'reservations', title: 'Reservations', order: 2 }, { startField: 'reservationTime', titleField: '
|
|
169
|
+
schedulerScreen(orders, { id: 'reservations', title: 'Reservations', order: 2 }, { startField: 'reservationTime', titleField: 'customerPhone', colorField: 'status', resourceField: 'tableId', initialView: 'day' }),
|
|
170
170
|
detailScreen(tables, { id: 'table-detail', title: 'Table detail', order: 3 }, {
|
|
171
171
|
titleField: 'name', statusField: 'section', metricFields: ['seats'],
|
|
172
172
|
related: [{ entity: 'orders', foreignKey: 'tableId', label: 'Orders', titleField: 'customerPhone', subtitleField: 'total', dateField: 'reservationTime', statusField: 'status' }],
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a generated SvelteKit 2 app into a SvelteKit 3 app.
|
|
3
|
+
*
|
|
4
|
+
* SvelteKit 3 shipped on 2026-10-01. It requires Vite 8, and Vite 8's Rolldown
|
|
5
|
+
* bundler crashes inside StackBlitz's WebContainer ("Invalid atomic access
|
|
6
|
+
* index", webcontainer-core #2105 / #2107), which is why the Studio's default
|
|
7
|
+
* stays on SvelteKit 2 + Vite 7 (see the pin in packageJson()). A project opts
|
|
8
|
+
* in with `kit: 3` and gets this conversion on top of the normal emit.
|
|
9
|
+
*
|
|
10
|
+
* It is a transform over the finished file set rather than a flag threaded
|
|
11
|
+
* through every emitter, for two reasons. The SvelteKit 2 output stays byte for
|
|
12
|
+
* byte what it was, so the default carries no risk from this change. And the
|
|
13
|
+
* official `npx sv migrate sveltekit-3` is the oracle: it was run on a
|
|
14
|
+
* Studio-generated app, and every rewrite below reproduces what it did there,
|
|
15
|
+
* checked against SvelteKit 3's own type definitions where its docs and its
|
|
16
|
+
* types disagreed (they do on `json`, which is deprecated, not removed).
|
|
17
|
+
*
|
|
18
|
+
* What the migrator could not decide for us, and what was decided instead:
|
|
19
|
+
* - env vars must be declared in `src/env.ts`. The migrator's default schema
|
|
20
|
+
* falls back to '' and flags it for review. Every read in the generated app
|
|
21
|
+
* already handles a missing value (`|| 'dev-insecure-...'`, `?? ''`,
|
|
22
|
+
* `if (env.X)`), so '' is the right fallback and is stated here, not flagged.
|
|
23
|
+
* - `drizzle.config.ts` keeps `process.env`: drizzle-kit runs outside Vite,
|
|
24
|
+
* where `$app/env` does not exist. Only `env.X` (no `process.`) is rewritten.
|
|
25
|
+
*/
|
|
26
|
+
import type { GeneratedFile } from './scaffold.js';
|
|
27
|
+
/** Toolchain SvelteKit 3 requires. Minimums are from the v3 migration guide. */
|
|
28
|
+
export declare const SVELTEKIT_3_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
29
|
+
/**
|
|
30
|
+
* The first major of each adapter whose peerDependency is `@sveltejs/kit ^3`,
|
|
31
|
+
* read from npm on 2026-10-08. None of the v3 adapter behaviour changes (node's
|
|
32
|
+
* ORIGIN, vercel's edge runtime, cloudflare's platform APIs) reach Studio
|
|
33
|
+
* output, so this is a version bump only.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SVELTEKIT_3_ADAPTERS: Readonly<Record<string, string>>;
|
|
36
|
+
/** Every major version of SvelteKit the Studio can emit. */
|
|
37
|
+
export type KitVersion = 2 | 3;
|
|
38
|
+
export declare function toSvelteKit3(files: readonly GeneratedFile[]): GeneratedFile[];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/** Toolchain SvelteKit 3 requires. Minimums are from the v3 migration guide. */
|
|
2
|
+
export const SVELTEKIT_3_DEV_DEPENDENCIES = {
|
|
3
|
+
'@sveltejs/kit': '^3.0.0',
|
|
4
|
+
'@sveltejs/vite-plugin-svelte': '^7.0.0',
|
|
5
|
+
svelte: '^5.57.1',
|
|
6
|
+
'svelte-check': '^4.7.5',
|
|
7
|
+
typescript: '^6.0.0',
|
|
8
|
+
vite: '^8.0.12',
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The first major of each adapter whose peerDependency is `@sveltejs/kit ^3`,
|
|
12
|
+
* read from npm on 2026-10-08. None of the v3 adapter behaviour changes (node's
|
|
13
|
+
* ORIGIN, vercel's edge runtime, cloudflare's platform APIs) reach Studio
|
|
14
|
+
* output, so this is a version bump only.
|
|
15
|
+
*/
|
|
16
|
+
export const SVELTEKIT_3_ADAPTERS = {
|
|
17
|
+
'@sveltejs/adapter-auto': '^8.0.0',
|
|
18
|
+
'@sveltejs/adapter-node': '^6.0.0',
|
|
19
|
+
'@sveltejs/adapter-vercel': '^7.0.0',
|
|
20
|
+
'@sveltejs/adapter-netlify': '^7.0.1',
|
|
21
|
+
'@sveltejs/adapter-cloudflare': '^8.0.0',
|
|
22
|
+
};
|
|
23
|
+
const SOURCE = /\.(ts|js|svelte)$/;
|
|
24
|
+
export function toSvelteKit3(files) {
|
|
25
|
+
const paths = new Set(files.map((f) => f.path));
|
|
26
|
+
const adapter = adapterOf(files);
|
|
27
|
+
const envNames = new Set();
|
|
28
|
+
const out = [];
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
// svelte.config.js is gone: its adapter and preprocess move into vite.config.ts.
|
|
31
|
+
if (file.path === 'svelte.config.js')
|
|
32
|
+
continue;
|
|
33
|
+
if (file.path === 'package.json') {
|
|
34
|
+
out.push({ ...file, contents: packageJson3(file.contents) });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (file.path === 'vite.config.ts') {
|
|
38
|
+
out.push({ ...file, description: `Vite config, including SvelteKit (${adapter}).`, contents: viteConfig3(file.contents, adapter) });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (file.path === 'tsconfig.json') {
|
|
42
|
+
out.push({ ...file, contents: TSCONFIG_3 });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (SOURCE.test(file.path) && file.path !== 'drizzle.config.ts') {
|
|
46
|
+
const contents = rewriteSource(file.contents, paths, envNames);
|
|
47
|
+
out.push(contents === file.contents ? file : { ...file, contents });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
out.push(file);
|
|
51
|
+
}
|
|
52
|
+
if (envNames.size)
|
|
53
|
+
out.push(envModule([...envNames].sort()));
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/* ---------------------------------------------------------------- config */
|
|
57
|
+
function adapterOf(files) {
|
|
58
|
+
const pkg = files.find((f) => f.path === 'package.json');
|
|
59
|
+
if (!pkg)
|
|
60
|
+
return '@sveltejs/adapter-auto';
|
|
61
|
+
const dev = (JSON.parse(pkg.contents).devDependencies ?? {});
|
|
62
|
+
return Object.keys(dev).find((k) => k.startsWith('@sveltejs/adapter-')) ?? '@sveltejs/adapter-auto';
|
|
63
|
+
}
|
|
64
|
+
function packageJson3(contents) {
|
|
65
|
+
const pkg = JSON.parse(contents);
|
|
66
|
+
const dev = { ...(pkg.devDependencies ?? {}) };
|
|
67
|
+
for (const [name, range] of Object.entries(SVELTEKIT_3_DEV_DEPENDENCIES))
|
|
68
|
+
dev[name] = range;
|
|
69
|
+
for (const name of Object.keys(dev))
|
|
70
|
+
if (SVELTEKIT_3_ADAPTERS[name])
|
|
71
|
+
dev[name] = SVELTEKIT_3_ADAPTERS[name];
|
|
72
|
+
pkg.devDependencies = dev;
|
|
73
|
+
// `#lib` is a standard subpath import now, so Node and every tool resolve it,
|
|
74
|
+
// not only Vite. `#lib` alone points at an index; `#lib/*` is the common case.
|
|
75
|
+
pkg.imports = { '#lib': './src/lib/index.js', '#lib/*': './src/lib/*' };
|
|
76
|
+
return JSON.stringify(pkg, null, 2) + '\n';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The v2 vite.config.ts with SvelteKit's config folded into the plugin call.
|
|
80
|
+
* Everything else in the file (the dev-server watch exclusions) is kept.
|
|
81
|
+
*/
|
|
82
|
+
function viteConfig3(v2, adapter) {
|
|
83
|
+
const imports = `import adapter from '${adapter}'\n` +
|
|
84
|
+
`import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n`;
|
|
85
|
+
const withPlugin = v2.replace('sveltekit()', 'sveltekit({ preprocess: vitePreprocess(), adapter: adapter() })');
|
|
86
|
+
if (withPlugin === v2)
|
|
87
|
+
throw new Error('toSvelteKit3: vite.config.ts has no sveltekit() call to configure');
|
|
88
|
+
return imports + withPlugin;
|
|
89
|
+
}
|
|
90
|
+
// `$app/tsconfig` replaces the generated ./.svelte-kit/tsconfig.json and brings
|
|
91
|
+
// the compiler options that used to be listed here. Matches the migrator.
|
|
92
|
+
const TSCONFIG_3 = `{
|
|
93
|
+
"extends": "$app/tsconfig",
|
|
94
|
+
"compilerOptions": {
|
|
95
|
+
"sourceMap": true,
|
|
96
|
+
"strict": true
|
|
97
|
+
},
|
|
98
|
+
"include": ["src", "vite.config.ts"]
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
/* --------------------------------------------------------------- sources */
|
|
102
|
+
function rewriteSource(src, paths, envNames) {
|
|
103
|
+
let s = src;
|
|
104
|
+
s = rewriteLibImports(s, paths);
|
|
105
|
+
s = rewriteEnv(s, envNames);
|
|
106
|
+
s = rewriteKitImports(s);
|
|
107
|
+
// keepFocus + noScroll are removed. `reset: false` is defined in v3's types as
|
|
108
|
+
// "the current scroll position and focused element are left alone", which is
|
|
109
|
+
// exactly what the two of them did together.
|
|
110
|
+
s = s.replace(/\{\s*keepFocus:\s*true,\s*noScroll:\s*true\s*\}/g, '{ reset: false }');
|
|
111
|
+
// page.url is readonly in v3, so `page.url.searchParams` is a
|
|
112
|
+
// ReadonlyURLSearchParams, which the URLSearchParams constructor does not
|
|
113
|
+
// accept. The generated code only copies it, never mutates it, so copying
|
|
114
|
+
// from the query string is the same thing and type-checks on both majors.
|
|
115
|
+
// The official migrator lists this as a manual task; the server-sorted list
|
|
116
|
+
// screen hit it, and only `verify:app --all-paths` reaches that screen.
|
|
117
|
+
s = s.replace(/new URLSearchParams\(page\.url\.searchParams\)/g, 'new URLSearchParams(page.url.search)');
|
|
118
|
+
return s;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `$lib/x` -> `#lib/x.js`. Subpath imports need a real extension, and the right
|
|
122
|
+
* one depends on what the file actually is, so each specifier is resolved
|
|
123
|
+
* against the generated paths rather than guessed: `x.ts` and `x.svelte.ts`
|
|
124
|
+
* are imported as `.js` (TypeScript maps it back), anything already carrying
|
|
125
|
+
* its extension (`.svelte`, `.js`) is left as it is, and a directory resolves
|
|
126
|
+
* to its index.
|
|
127
|
+
*/
|
|
128
|
+
function rewriteLibImports(src, paths) {
|
|
129
|
+
return src.replace(/(['"`])\$lib\/([^'"`]+)\1/g, (whole, q, spec) => {
|
|
130
|
+
const base = `src/lib/${spec}`;
|
|
131
|
+
if (paths.has(base))
|
|
132
|
+
return `${q}#lib/${spec}${q}`;
|
|
133
|
+
if (paths.has(`${base}.ts`) || paths.has(`${base}.js`))
|
|
134
|
+
return `${q}#lib/${spec}.js${q}`;
|
|
135
|
+
if (paths.has(`${base}/index.ts`) || paths.has(`${base}/index.js`))
|
|
136
|
+
return `${q}#lib/${spec}/index.js${q}`;
|
|
137
|
+
// Not a file this app generated (a user-added module, or one emitted
|
|
138
|
+
// conditionally elsewhere). `.js` is what the migrator writes for a TS
|
|
139
|
+
// source, which is overwhelmingly the case in a Studio app.
|
|
140
|
+
return `${q}#lib/${spec}.js${q}`;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* `import { env } from '$env/dynamic/private'` + `env.X`
|
|
145
|
+
* -> `import { X } from '$app/env/private'` + `X`
|
|
146
|
+
* Every name read is collected so src/env.ts can declare it, which v3 requires.
|
|
147
|
+
*/
|
|
148
|
+
function rewriteEnv(src, envNames) {
|
|
149
|
+
if (!/from\s+['"]\$env\/dynamic\/private['"]/.test(src))
|
|
150
|
+
return src;
|
|
151
|
+
const used = new Set();
|
|
152
|
+
// `(?<![.\w])` keeps `process.env.X` untouched; only the imported `env` changes.
|
|
153
|
+
const body = src.replace(/(?<![.\w])env\.([A-Z_][A-Z0-9_]*)/g, (_m, name) => {
|
|
154
|
+
used.add(name);
|
|
155
|
+
return name;
|
|
156
|
+
});
|
|
157
|
+
for (const n of used)
|
|
158
|
+
envNames.add(n);
|
|
159
|
+
const names = [...used].sort();
|
|
160
|
+
return body.replace(/import\s*\{\s*env\s*\}\s*from\s*(['"])\$env\/dynamic\/private\1/, names.length ? `import { ${names.join(', ')} } from '$app/env/private'` : '');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Types that moved out of `@sveltejs/kit`, and the deprecated `json` helper.
|
|
164
|
+
* `fail`, `redirect` and `error` stay where they are.
|
|
165
|
+
*/
|
|
166
|
+
function rewriteKitImports(src) {
|
|
167
|
+
let s = src;
|
|
168
|
+
s = s.replace(/import\s+type\s*\{\s*Handle\s*\}\s*from\s*(['"])@sveltejs\/kit\1/g, `import type { Handle } from '@sveltejs/kit/hooks'`);
|
|
169
|
+
s = s.replace(/import\s+type\s*\{\s*SubmitFunction\s*\}\s*from\s*(['"])@sveltejs\/kit\1/g, `import type { SubmitFunction } from '$app/forms'`);
|
|
170
|
+
// json(...) -> Response.json(...): same (data, init) signature. Only when
|
|
171
|
+
// `json` really is the kit helper - a file importing it from somewhere else,
|
|
172
|
+
// or calling res.json(), is not touched.
|
|
173
|
+
const kitImport = /import\s*\{([^}]*)\}\s*from\s*(['"])@sveltejs\/kit\2/;
|
|
174
|
+
const names = (s.match(kitImport)?.[1] ?? '').split(',').map((x) => x.trim());
|
|
175
|
+
if (names.includes('json')) {
|
|
176
|
+
s = s.replace(/(?<![.\w])json\(/g, 'Response.json(');
|
|
177
|
+
const rest = names.filter((x) => x && x !== 'json');
|
|
178
|
+
s = s.replace(kitImport, rest.length ? `import { ${rest.join(', ')} } from '@sveltejs/kit'` : '');
|
|
179
|
+
}
|
|
180
|
+
return s;
|
|
181
|
+
}
|
|
182
|
+
/* ------------------------------------------------------------------- env */
|
|
183
|
+
function envModule(names) {
|
|
184
|
+
const decls = names.map((n) => ` ${n}: { schema: (input) => input ?? '' },`).join('\n');
|
|
185
|
+
return {
|
|
186
|
+
path: 'src/env.ts',
|
|
187
|
+
description: 'Environment variables the app reads (SvelteKit 3 requires declaring them).',
|
|
188
|
+
contents: `import { defineEnvVars } from '@sveltejs/kit/env'\n\n` +
|
|
189
|
+
`// SvelteKit 3 reads only the variables declared here. A missing one comes\n` +
|
|
190
|
+
`// through as '' - every read in this app already falls back on that\n` +
|
|
191
|
+
`// (a dev default, '?? ""', or an if), so that is the intended behaviour.\n` +
|
|
192
|
+
`export const variables = defineEnvVars({\n${decls}\n})\n`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -6943,14 +6943,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6943
6943
|
"key": "columnVirtualization",
|
|
6944
6944
|
"label": "Column Virtualization",
|
|
6945
6945
|
"type": "boolean",
|
|
6946
|
-
"description": "Render only the columns in view, the horizontal counterpart of `virtualization`. **On by default**, which is what makes a 100-column grid scroll smoothly.
|
|
6946
|
+
"description": "Render only the columns in view, the horizontal counterpart of `virtualization`. **On by default**, which is what makes a 100-column grid scroll smoothly. Pinned columns stay rendered while it is on: the pinned-left and pinned-right columns are drawn as their own runs either side of the virtual window, so a wide grid with pinned edges still renders only the columns in view plus the pinned ones.",
|
|
6947
6947
|
"group": "common"
|
|
6948
6948
|
},
|
|
6949
6949
|
{
|
|
6950
6950
|
"key": "columnOverscan",
|
|
6951
6951
|
"label": "Column Overscan",
|
|
6952
6952
|
"type": "number",
|
|
6953
|
-
"description": "
|
|
6953
|
+
"description": "Columns kept rendered beyond the viewport when `columnVirtualization` is on: this many ahead of a horizontal scroll at all times, one behind it. The rendered columns move three at a time, so right after a move up to `columnOverscan + 2` are rendered ahead. Defaults to 3.",
|
|
6954
6954
|
"group": "common"
|
|
6955
6955
|
},
|
|
6956
6956
|
{
|
|
@@ -6964,7 +6964,7 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6964
6964
|
"key": "initialColumnPinning",
|
|
6965
6965
|
"label": "Initial Column Pinning",
|
|
6966
6966
|
"type": "json",
|
|
6967
|
-
"description": "Columns pinned to the left/right edge on mount. Each entry is a column id (matches `ColumnDef.field` when no explicit id is set). The internal pinning state is seeded once at mount; user-driven pinning via the column menu still works and overrides this default.
|
|
6967
|
+
"description": "Columns pinned to the left/right edge on mount. Each entry is a column id (matches `ColumnDef.field` when no explicit id is set). The internal pinning state is seeded once at mount; user-driven pinning via the column menu still works and overrides this default. Works with column virtualization on (the default).",
|
|
6968
6968
|
"group": "common"
|
|
6969
6969
|
},
|
|
6970
6970
|
{
|
|
@@ -7245,7 +7245,7 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7245
7245
|
"key": "conditionalFormats",
|
|
7246
7246
|
"label": "Conditional Formats",
|
|
7247
7247
|
"type": "json",
|
|
7248
|
-
"description": "Excel-style conditional formatting. A list of value-driven rules that color cells: `colorScale` (gradient across the column range), `dataBar` (in-cell proportional bar), `iconSet` (arrows / traffic / triangles by threshold), and `rule` (apply a style when a predicate matches). Scope a format to specific columns with `columns: [...]`, or omit it to apply to every column. Later entries win on conflict.",
|
|
7248
|
+
"description": "Excel-style conditional formatting. A list of value-driven rules that color cells: `colorScale` (gradient across the column range), `dataBar` (in-cell proportional bar), `iconSet` (arrows / traffic / triangles by threshold), and `rule` (apply a style when a predicate matches). Scope a format to specific columns with `columns: [...]`, or omit it to apply to every column. Later entries win on conflict. `colorScale` and `dataBar` apply to data rows only: group, footer and grand-total rows carry subtotals on a different scale, so they keep the `rule` and `iconSet` formats and drop the scaled ones. On a server row model, give scaled formats `minValue` / `maxValue`; the grid does not scan rows it has not loaded for a range.",
|
|
7249
7249
|
"group": "common"
|
|
7250
7250
|
},
|
|
7251
7251
|
{
|
|
@@ -7369,6 +7369,17 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7369
7369
|
"description": "Merged cells: rectangles drawn as one cell, a spreadsheet's Merge & Center. Each entry is the top-left cell (display indices) and how many rows and columns it covers. The origin's td takes the span and shows the origin's value; the covered cells are not drawn, a selection grows to whole merges, the active cell inside a merge is its origin, and the arrow keys step over a merge as one cell. A merge that crosses the frozen boundary or the rendered window is drawn in parts, one per band. The grid does not write into covered cells on its own; a consumer that merges cells keeps them empty or marks them read-only through the column's `editable`.",
|
|
7370
7370
|
"group": "common"
|
|
7371
7371
|
},
|
|
7372
|
+
{
|
|
7373
|
+
"key": "rowLayout",
|
|
7374
|
+
"label": "Row Layout",
|
|
7375
|
+
"type": "select",
|
|
7376
|
+
"options": [
|
|
7377
|
+
"flex",
|
|
7378
|
+
"table"
|
|
7379
|
+
],
|
|
7380
|
+
"description": "How rows are laid out. `'flex'` (the default) draws each row as a line of fixed-width cells, so adding or removing cells in one row - what a horizontal scroll does every frame - lays out that row only. `'table'` uses the HTML table algorithm, which re-lays every row on such a change but can draw a merged cell spanning rows; the grid switches to it on its own when `mergedCells` has one.",
|
|
7381
|
+
"group": "common"
|
|
7382
|
+
},
|
|
7372
7383
|
{
|
|
7373
7384
|
"key": "pinnedBottomRows",
|
|
7374
7385
|
"label": "Pinned Bottom Rows",
|
package/dist/version.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
|
|
12
12
|
*/
|
|
13
|
-
export declare const SVGRID_VERSION = "3.1.
|
|
13
|
+
export declare const SVGRID_VERSION = "3.1.5";
|
|
14
14
|
/**
|
|
15
15
|
* The version of `@svgrid/grid`, which releases on its own line: enterprise is
|
|
16
16
|
* on 3.1.x while the grid is on 3.0.x.
|
|
@@ -23,4 +23,4 @@ export declare const SVGRID_VERSION = "3.1.4";
|
|
|
23
23
|
*
|
|
24
24
|
* `version.test.ts` asserts this matches packages/grid/package.json.
|
|
25
25
|
*/
|
|
26
|
-
export declare const GRID_VERSION = "3.0.
|
|
26
|
+
export declare const GRID_VERSION = "3.0.9";
|
package/dist/version.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
|
|
12
12
|
*/
|
|
13
|
-
export const SVGRID_VERSION = '3.1.
|
|
13
|
+
export const SVGRID_VERSION = '3.1.5';
|
|
14
14
|
/**
|
|
15
15
|
* The version of `@svgrid/grid`, which releases on its own line: enterprise is
|
|
16
16
|
* on 3.1.x while the grid is on 3.0.x.
|
|
@@ -23,4 +23,4 @@ export const SVGRID_VERSION = '3.1.4';
|
|
|
23
23
|
*
|
|
24
24
|
* `version.test.ts` asserts this matches packages/grid/package.json.
|
|
25
25
|
*/
|
|
26
|
-
export const GRID_VERSION = '3.0.
|
|
26
|
+
export const GRID_VERSION = '3.0.9';
|
package/dist/watermark.js
CHANGED
|
@@ -32,10 +32,40 @@ export function emitUnlicensedNudge() {
|
|
|
32
32
|
queueMicrotask(() => attachToAllGrids());
|
|
33
33
|
// Watch for grids that mount later (route changes, dynamic loads).
|
|
34
34
|
if (!observer) {
|
|
35
|
-
observer = new MutationObserver(
|
|
35
|
+
observer = new MutationObserver(onMutations);
|
|
36
36
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The observer sees every DOM change on the page, and a scrolling grid
|
|
41
|
+
* changes the DOM every frame (cells in, cells out). Changes inside a grid
|
|
42
|
+
* that already has its watermark cannot add a grid, so they are skipped, and
|
|
43
|
+
* the rest wait for one check per frame instead of a document-wide query per
|
|
44
|
+
* mutation batch - which had made an unlicensed grid scroll measurably slower.
|
|
45
|
+
*/
|
|
46
|
+
let checkScheduled = false;
|
|
47
|
+
function onMutations(records) {
|
|
48
|
+
for (const record of records) {
|
|
49
|
+
const target = record.target;
|
|
50
|
+
if (target.nodeType === 1 && target.closest?.(`.sv-grid-root[${NUDGED_ATTR}]`))
|
|
51
|
+
continue;
|
|
52
|
+
scheduleCheck();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function scheduleCheck() {
|
|
57
|
+
if (checkScheduled)
|
|
58
|
+
return;
|
|
59
|
+
checkScheduled = true;
|
|
60
|
+
const run = () => {
|
|
61
|
+
checkScheduled = false;
|
|
62
|
+
attachToAllGrids();
|
|
63
|
+
};
|
|
64
|
+
if (typeof requestAnimationFrame === 'function')
|
|
65
|
+
requestAnimationFrame(run);
|
|
66
|
+
else
|
|
67
|
+
setTimeout(run, 16);
|
|
68
|
+
}
|
|
39
69
|
export function dismissUnlicensedNudge() {
|
|
40
70
|
if (observer) {
|
|
41
71
|
observer.disconnect();
|