diff --git a/theatre/studio/src/Studio.ts b/theatre/studio/src/Studio.ts index ee9e164..0c77dad 100644 --- a/theatre/studio/src/Studio.ts +++ b/theatre/studio/src/Studio.ts @@ -25,6 +25,34 @@ import checkForUpdates from './checkForUpdates' export type CoreExports = typeof _coreExports +const STUDIO_NOT_INITIALIZED_MESSAGE = `You seem to have imported '@theatre/studio' but haven't initialized it. You can initialize the studio by: +\`\`\` +import studio from '@theatre/studio' +studio.initialize() +\`\`\` + +* If you didn't mean to import '@theatre/studio', this means that your bundler is not tree-shaking it. This is most likely a bundler misconfiguration. + +* If you meant to import '@theatre/studio' without showing its UI, you can do that by running: + +\`\`\` +import studio from '@theatre/studio' +studio.initialize() +studio.ui.hide() +\`\`\` +` + +const STUDIO_INITIALIZED_LATE_MSG = `You seem to have imported '@theatre/studio' but called \`studio.initialize()\` after some delay. +Theatre.js projects remain in pending mode (won't play their sequences) until the studio is initialized, so you should place the \`studio.initialize()\` line right after the import line: + +\`\`\` +import studio from '@theatre/studio' +// ... and other imports + +studio.initialize() +\`\`\` +` + export class Studio { readonly ui!: UI readonly publicApi: IStudio @@ -41,9 +69,23 @@ export class Studio { private readonly _cache = new SimpleCache() readonly paneManager: PaneManager + /** + * An atom holding the exports of '\@theatre/core'. Will be undefined if '\@theatre/core' is never imported + */ private _coreAtom = new Atom<{core?: CoreExports}>({}) + + /** + * A Deferred that will resolve once studio is initialized (and its state is read from storage) + */ private readonly _initializedDeferred: Deferred = defer() + /** + * Tracks whether studio.initialize() is called. + */ private _initializeFnCalled = false + /** + * Will be set to true if studio.initialize() isn't called after 100ms. + */ + private _didWarnAboutNotInitializing = false get atomP() { return this._store.atomP @@ -60,19 +102,27 @@ export class Studio { this._attachToIncomingProjects() this.paneManager = new PaneManager(this) - /** - * @remarks - * TODO If studio.initialize() is not called within a few milliseconds, - * we should console.warn() the user that `@theatre/studio` is still in - * their bundle. This way we can avoid issues like - * [this](https://discord.com/channels/870988717190426644/892469755225710642/892479678797971486). - */ + setTimeout(() => { + if (!this._initializeFnCalled) { + console.error(STUDIO_NOT_INITIALIZED_MESSAGE) + this._didWarnAboutNotInitializing = true + } + }, 100) } async initialize(opts?: Parameters[0]) { if (this._initializeFnCalled) { + console.warn( + `\`studio.initialize()\` is already called. You only need to call \`studio.initialize()\` once.`, + ) return this._initializedDeferred.promise } + this._initializeFnCalled = true + + if (this._didWarnAboutNotInitializing) { + console.warn(STUDIO_INITIALIZED_LATE_MSG) + } + const storeOpts: Parameters[0] = { persistenceKey: 'theatre-0.4', usePersistentStorage: true,