@@ -33,6 +33,11 @@ define(function (require, exports, module) {
3333 StringUtils = require ( "utils/StringUtils" ) ,
3434 Metrics = require ( "utils/Metrics" ) ,
3535 SidebarView = require ( "project/SidebarView" ) ,
36+ ProjectManager = require ( "project/ProjectManager" ) ,
37+ EditorManager = require ( "editor/EditorManager" ) ,
38+ CommandManager = require ( "command/CommandManager" ) ,
39+ Commands = require ( "command/Commands" ) ,
40+ WorkspaceManager = require ( "view/WorkspaceManager" ) ,
3641 CentralControlBar = require ( "view/CentralControlBar" ) ;
3742
3843 // Capture the kernel trust ring at module-load time — it's deleted from
@@ -102,7 +107,7 @@ define(function (require, exports, module) {
102107 }
103108 }
104109
105- const TOTAL_STEPS = 3 ;
110+ const TOTAL_STEPS = 4 ;
106111
107112 function _ensureOverlay ( ) {
108113 if ( $overlay ) {
@@ -279,16 +284,105 @@ define(function (require, exports, module) {
279284 _ensureSidebarVisible ( ) ;
280285 const $newBtn = $ ( "#newProject" ) ;
281286 if ( ! $newBtn . length ) {
282- // No new-project button — tour is effectively done.
283- _markComplete ( ) ;
284- _teardown ( ) ;
287+ // No new-project button — skip to the live-preview step instead
288+ // of giving up on the tour entirely.
289+ _runStep4 ( ) ;
285290 return ;
286291 }
287292 _ensureOverlay ( ) ;
288293 _trackTarget ( $newBtn , "right" ) ;
289294 _setStep ( 3 ) ;
290295 Metrics . countEvent ( Metrics . EVENT_TYPE . GUIDE , "tour" , "step3" ) ;
291296 _setText ( Strings . PHOENIX_TOUR_NEW_PROJECT ) ;
297+ _setActions ( [
298+ {
299+ label : Strings . PHOENIX_TOUR_NEXT_BTN ,
300+ kind : "primary" ,
301+ onClick : function ( ) {
302+ _runStep4 ( ) ;
303+ }
304+ }
305+ ] ) ;
306+ // Intentionally do NOT advance on a real click of the target — only
307+ // the Next button advances.
308+ }
309+
310+ /**
311+ * Bring the workspace to a state where the live-preview Edit Mode button
312+ * is visible and meaningful: the welcome project must be open, the
313+ * active editor file must be one the preview can render (the welcome
314+ * project's index.html or phoenix-pro.html), and the LP panel must be
315+ * showing. Each branch is a no-op when already true.
316+ */
317+ async function _ensureLivePreviewReady ( ) {
318+ const welcomeRoot = ProjectManager . getWelcomeProjectPath ( ) ;
319+
320+ // 1. Switch to the welcome project if we're not already there.
321+ const currentRoot = ProjectManager . getProjectRoot ( ) ;
322+ if ( ! currentRoot || currentRoot . fullPath !== welcomeRoot ) {
323+ await new Promise ( function ( resolve ) {
324+ ProjectManager . openProject ( welcomeRoot )
325+ . done ( resolve ) . fail ( resolve ) ;
326+ } ) ;
327+ }
328+
329+ // 2. Make sure the active file is one the LP can render. Prefer
330+ // phoenix-pro.html (Pro flow) and fall back to index.html.
331+ const proPath = welcomeRoot + "phoenix-pro.html" ;
332+ const indexPath = welcomeRoot + "index.html" ;
333+ const editor = EditorManager . getActiveEditor ( ) ;
334+ const currentFile = editor && editor . document && editor . document . file
335+ ? editor . document . file . fullPath : null ;
336+ if ( currentFile !== proPath && currentFile !== indexPath ) {
337+ let target = null ;
338+ try {
339+ if ( await Phoenix . VFS . existsAsync ( proPath ) ) {
340+ target = proPath ;
341+ } else if ( await Phoenix . VFS . existsAsync ( indexPath ) ) {
342+ target = indexPath ;
343+ }
344+ } catch ( e ) { /* fall through with target=null */ }
345+ if ( target ) {
346+ await new Promise ( function ( resolve ) {
347+ CommandManager . execute ( Commands . FILE_OPEN , { fullPath : target } )
348+ . done ( resolve ) . fail ( resolve ) ;
349+ } ) ;
350+ }
351+ }
352+
353+ // 3. Open the live-preview panel if it isn't visible.
354+ const lpPanel = WorkspaceManager . getPanelForID ( "live-preview-panel" ) ;
355+ if ( ! lpPanel || ! lpPanel . isVisible ( ) ) {
356+ await new Promise ( function ( resolve ) {
357+ CommandManager . execute ( Commands . FILE_LIVE_FILE_PREVIEW )
358+ . done ( resolve ) . fail ( resolve ) ;
359+ } ) ;
360+ }
361+ }
362+
363+ async function _runStep4 ( ) {
364+ _ensureSidebarVisible ( ) ;
365+ try {
366+ await _ensureLivePreviewReady ( ) ;
367+ } catch ( e ) { /* best-effort prep — proceed and let the rect-zero
368+ RAF gate sort out a missing target */ }
369+
370+ const $btn = $ ( "#previewModeLivePreviewButton" ) ;
371+ if ( ! $btn . length ) {
372+ // LP panel never came up (custom server, unsupported file, etc.)
373+ // — finalize the tour rather than stalling on a missing target.
374+ _markComplete ( ) ;
375+ _teardown ( ) ;
376+ return ;
377+ }
378+ _ensureOverlay ( ) ;
379+ // LP panel sits on the right edge; keep the default tooltip
380+ // placement (lower-right of the ring) so the tooltip extends back
381+ // into the panel area rather than off the right edge of the viewport.
382+ _trackTarget ( $btn , "left" ) ;
383+ _setStep ( 4 ) ;
384+ Metrics . countEvent ( Metrics . EVENT_TYPE . GUIDE , "tour" , "step4" ) ;
385+ _setText ( Strings . PHOENIX_TOUR_EDIT_MODE ) ;
292386 _setActions ( [
293387 {
294388 label : Strings . PHOENIX_TOUR_DISMISS_BTN ,
@@ -300,8 +394,6 @@ define(function (require, exports, module) {
300394 }
301395 }
302396 ] ) ;
303- // Intentionally do NOT end on a real click of the target — only the
304- // Dismiss button ends the tour.
305397 }
306398
307399 function _shouldRun ( ) {
0 commit comments