Skip to content

Commit 465b71c

Browse files
refactor: improving gh syncs.
1 parent eabcc1c commit 465b71c

12 files changed

Lines changed: 766 additions & 57 deletions

playwright/github-pr-drawer.spec.ts

Lines changed: 544 additions & 28 deletions
Large diffs are not rendered by default.

src/app.js

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from './modules/app-core/workspace-local-helpers.js'
3131
import { createWorkspaceEditorHelpers } from './modules/app-core/workspace-editor-helpers.js'
3232
import { createEditedIndicatorVisibilityController } from './modules/app-core/edited-indicator-visibility-controller.js'
33+
import { createPublishTrailingNewlineNormalizer } from './modules/app-core/publish-trailing-newline-normalizer.js'
3334
import { createLayoutDiagnosticsSetup } from './modules/app-core/layout-diagnostics-setup.js'
3435
import { createWorkspaceControllersSetup } from './modules/app-core/workspace-controllers-setup.js'
3536
import { createGitHubWorkflowsSetup } from './modules/app-core/github-workflows-setup.js'
@@ -393,6 +394,8 @@ const githubAiContextState = {
393394
hasSyncedActivePrEditorContent: false,
394395
}
395396

397+
let workspacePrContextState = 'inactive'
398+
396399
let chatDrawerController = {
397400
setOpen: () => {},
398401
setSelectedRepository: () => {},
@@ -492,11 +495,16 @@ const getCurrentGitHubToken = () => githubAiContextState.token ?? byotControls.g
492495
const getCurrentSelectedRepository = () =>
493496
githubAiContextState.selectedRepository ?? byotControls.getSelectedRepository()
494497

498+
const getCurrentSelectedRepositoryFullName = () =>
499+
getCurrentSelectedRepository()?.fullName ?? ''
500+
495501
const getWorkspaceContextSnapshot = createWorkspaceContextSnapshotGetter({
496-
getCurrentSelectedRepository,
502+
getCurrentSelectedRepository: getCurrentSelectedRepositoryFullName,
497503
githubPrBaseBranch,
498504
githubPrHeadBranch,
499505
githubPrTitle,
506+
getActivePrContext: () => githubAiContextState.activePrContext,
507+
getPrContextState: () => workspacePrContextState,
500508
})
501509

502510
let loadedComponentTabId = 'component'
@@ -597,11 +605,8 @@ const ensureWorkspaceTabsShape = createEnsureWorkspaceTabsShape({
597605
const buildWorkspaceTabsSnapshot = () =>
598606
workspaceSyncController.buildWorkspaceTabsSnapshot()
599607

600-
const reconcileWorkspaceTabsWithPushUpdates = fileUpdates =>
601-
workspaceSyncController.reconcileWorkspaceTabsWithPushUpdates(fileUpdates)
602-
603-
const getWorkspacePrFileCommits = () =>
604-
workspaceSyncController.getWorkspacePrFileCommits()
608+
const getWorkspacePrFileCommits = options =>
609+
workspaceSyncController.getWorkspacePrFileCommits(options)
605610

606611
const getEditorSyncTargets = () => workspaceSyncController.getEditorSyncTargets()
607612

@@ -634,7 +639,7 @@ const {
634639
getActiveWorkspaceCreatedAt: () => activeWorkspaceCreatedAt,
635640
setActiveWorkspaceRecordId: value => (activeWorkspaceRecordId = value),
636641
setActiveWorkspaceCreatedAt: value => (activeWorkspaceCreatedAt = value),
637-
getCurrentSelectedRepository,
642+
getCurrentSelectedRepository: getCurrentSelectedRepositoryFullName,
638643
getActiveWorkspaceRecordId: () => activeWorkspaceRecordId,
639644
setIsApplyingWorkspaceSnapshot: value => (isApplyingWorkspaceSnapshot = value),
640645
ensureWorkspaceTabsShape,
@@ -700,6 +705,41 @@ editedIndicatorVisibilityController.setRefreshHandlers({
700705
renderWorkspaceTabs,
701706
})
702707

708+
const normalizeWorkspaceEditorsTrailingNewlineAfterPublish =
709+
createPublishTrailingNewlineNormalizer({
710+
workspaceTabsState,
711+
getLoadedTabIds: () => [loadedComponentTabId, loadedStylesTabId],
712+
getJsxSource: () => getJsxSource(),
713+
getCssSource: () => getCssSource(),
714+
setJsxSource,
715+
setCssSource,
716+
setSuppressEditorChangeSideEffects: value => {
717+
suppressEditorChangeSideEffects = value
718+
},
719+
queueWorkspaceSave: () => queueWorkspaceSave(),
720+
})
721+
722+
const reconcileWorkspaceTabsWithPushUpdates = fileUpdates => {
723+
normalizeWorkspaceEditorsTrailingNewlineAfterPublish()
724+
return workspaceSyncController.reconcileWorkspaceTabsWithPushUpdates(fileUpdates)
725+
}
726+
727+
const setWorkspacePrContextState = nextState => {
728+
if (typeof nextState !== 'string' || !nextState.trim()) {
729+
return
730+
}
731+
732+
workspacePrContextState = nextState.trim()
733+
}
734+
735+
const persistWorkspacePrContextState = nextState => {
736+
setWorkspacePrContextState(nextState)
737+
queueWorkspaceSave()
738+
void flushWorkspaceSave().catch(() => {
739+
/* Save failures are already surfaced through saver onError. */
740+
})
741+
}
742+
703743
const githubWorkflows = createGitHubWorkflowsSetup({
704744
factories: {
705745
createGitHubPrEditorSyncController,
@@ -774,9 +814,20 @@ const githubWorkflows = createGitHubWorkflowsSetup({
774814
getStyleMode: () => styleMode.value,
775815
getActivePrContextSyncKey,
776816
prContextUi,
777-
onPrContextStateChange: () => {
817+
onPrContextStateChange: activeContext => {
818+
if (activeContext?.prTitle) {
819+
setWorkspacePrContextState('active')
820+
} else if (workspacePrContextState === 'active') {
821+
setWorkspacePrContextState('inactive')
822+
}
778823
editedIndicatorVisibilityController.refreshIndicators()
779824
},
825+
onPrContextClosed: () => {
826+
persistWorkspacePrContextState('closed')
827+
},
828+
onPrContextDisconnected: () => {
829+
persistWorkspacePrContextState('disconnected')
830+
},
780831
getTokenForVisibility: () => githubAiContextState.token,
781832
closeWorkspacesDrawer: () => {
782833
void workspacesDrawerController?.setOpen(false)

src/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,8 +920,8 @@ <h2 id="open-pr-title">Open Pull Request</h2>
920920
class="github-pr-field github-pr-field--full github-pr-field--checkbox"
921921
for="github-pr-include-app-wrapper"
922922
>
923-
<input id="github-pr-include-app-wrapper" type="checkbox" />
924-
<span>Include entry tab source in committed output</span>
923+
<input id="github-pr-include-app-wrapper" type="checkbox" checked />
924+
<span>Include entry tab</span>
925925
</label>
926926

927927
<label class="github-pr-field github-pr-field--full" for="github-pr-title">

src/modules/app-core/github-workflows-setup.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const createGitHubWorkflowsSetup = ({
3535
getActivePrContextSyncKey: runtime.getActivePrContextSyncKey,
3636
prContextUi: runtime.prContextUi,
3737
onPrContextStateChange: runtime.onPrContextStateChange,
38+
onPrContextClosed: runtime.onPrContextClosed,
39+
onPrContextDisconnected: runtime.onPrContextDisconnected,
3840
getTokenForVisibility: runtime.getTokenForVisibility,
3941
closeWorkspacesDrawer: runtime.closeWorkspacesDrawer,
4042
getActivePrEditorSyncKey: runtime.getActivePrEditorSyncKey,

src/modules/app-core/github-workflows.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ const initializeGitHubWorkflows = ({
5959
getActivePrContextSyncKey,
6060
prContextUi,
6161
onPrContextStateChange,
62+
onPrContextClosed,
63+
onPrContextDisconnected,
6264
getTokenForVisibility,
6365
closeWorkspacesDrawer,
6466
getActivePrEditorSyncKey,
@@ -323,6 +325,9 @@ const initializeGitHubWorkflows = ({
323325
: 'Closed pull request on GitHub and cleared active context.',
324326
'neutral',
325327
)
328+
if (typeof onPrContextClosed === 'function') {
329+
onPrContextClosed()
330+
}
326331
showAppToast(
327332
reference
328333
? `Closed pull request on GitHub and cleared active context (${reference}).`
@@ -364,6 +369,9 @@ const initializeGitHubWorkflows = ({
364369
: 'Disconnected PR context. Pull request remains open on GitHub.',
365370
'neutral',
366371
)
372+
if (typeof onPrContextDisconnected === 'function') {
373+
onPrContextDisconnected()
374+
}
367375
},
368376
})
369377
})
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const toContentWithTrailingNewline = value => {
2+
if (typeof value !== 'string' || value.length === 0 || value.endsWith('\n')) {
3+
return value
4+
}
5+
6+
return `${value}\n`
7+
}
8+
9+
const createPublishTrailingNewlineNormalizer = ({
10+
workspaceTabsState,
11+
getLoadedTabIds,
12+
getJsxSource,
13+
getCssSource,
14+
setJsxSource,
15+
setCssSource,
16+
setSuppressEditorChangeSideEffects,
17+
queueWorkspaceSave,
18+
}) => {
19+
return () => {
20+
const tabs = workspaceTabsState.getTabs()
21+
const activeTabId = workspaceTabsState.getActiveTabId()
22+
const loadedTabIds = new Set(
23+
(Array.isArray(getLoadedTabIds?.()) ? getLoadedTabIds() : []).filter(Boolean),
24+
)
25+
const now = Date.now()
26+
let didUpdateTabs = false
27+
28+
const nextTabs = tabs.map(tab => {
29+
if (!loadedTabIds.has(tab?.id)) {
30+
return tab
31+
}
32+
33+
const currentContent = typeof tab?.content === 'string' ? tab.content : ''
34+
const nextContent = toContentWithTrailingNewline(currentContent)
35+
if (nextContent === currentContent) {
36+
return tab
37+
}
38+
39+
didUpdateTabs = true
40+
return {
41+
...tab,
42+
content: nextContent,
43+
syncedContent: nextContent,
44+
isDirty: false,
45+
lastModified: now,
46+
}
47+
})
48+
49+
if (didUpdateTabs) {
50+
workspaceTabsState.replaceTabs({ tabs: nextTabs, activeTabId })
51+
}
52+
53+
const nextJsxSource = toContentWithTrailingNewline(getJsxSource())
54+
if (nextJsxSource !== getJsxSource()) {
55+
setSuppressEditorChangeSideEffects(true)
56+
try {
57+
setJsxSource(nextJsxSource)
58+
} finally {
59+
setSuppressEditorChangeSideEffects(false)
60+
}
61+
}
62+
63+
const nextCssSource = toContentWithTrailingNewline(getCssSource())
64+
if (nextCssSource !== getCssSource()) {
65+
setSuppressEditorChangeSideEffects(true)
66+
try {
67+
setCssSource(nextCssSource)
68+
} finally {
69+
setSuppressEditorChangeSideEffects(false)
70+
}
71+
}
72+
73+
if (didUpdateTabs) {
74+
queueWorkspaceSave()
75+
}
76+
}
77+
}
78+
79+
export { createPublishTrailingNewlineNormalizer }

src/modules/app-core/workspace-controllers-setup.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ const createWorkspaceControllersSetup = ({
143143
setActiveWorkspaceTab,
144144
makeUniqueTabPath,
145145
createWorkspaceTabId,
146+
getShouldShowEditedDesign,
146147
})
147148

148149
const beginWorkspaceTabRenameDelegate = tabId =>

src/modules/app-core/workspace-local-helpers.js

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,34 @@ const createWorkspaceContextSnapshotGetter =
44
githubPrBaseBranch,
55
githubPrHeadBranch,
66
githubPrTitle,
7+
getActivePrContext,
8+
getPrContextState,
79
}) =>
8-
() => ({
9-
repositoryFullName: getCurrentSelectedRepository(),
10-
baseBranch:
11-
typeof githubPrBaseBranch?.value === 'string'
12-
? githubPrBaseBranch.value.trim()
13-
: '',
14-
headBranch:
15-
typeof githubPrHeadBranch?.value === 'string'
16-
? githubPrHeadBranch.value.trim()
17-
: '',
18-
prTitle: typeof githubPrTitle?.value === 'string' ? githubPrTitle.value.trim() : '',
19-
})
10+
() => {
11+
const activePrContext =
12+
typeof getActivePrContext === 'function' ? getActivePrContext() : null
13+
const prNumber =
14+
typeof activePrContext?.pullRequestNumber === 'number' &&
15+
Number.isFinite(activePrContext.pullRequestNumber)
16+
? activePrContext.pullRequestNumber
17+
: null
18+
19+
return {
20+
repositoryFullName: getCurrentSelectedRepository(),
21+
baseBranch:
22+
typeof githubPrBaseBranch?.value === 'string'
23+
? githubPrBaseBranch.value.trim()
24+
: '',
25+
headBranch:
26+
typeof githubPrHeadBranch?.value === 'string'
27+
? githubPrHeadBranch.value.trim()
28+
: '',
29+
prTitle: typeof githubPrTitle?.value === 'string' ? githubPrTitle.value.trim() : '',
30+
prNumber,
31+
prContextState:
32+
typeof getPrContextState === 'function' ? getPrContextState() : 'inactive',
33+
}
34+
}
2035

2136
const toStyleModeForTabLanguage = ({ language, toNonEmptyWorkspaceText }) => {
2237
const normalized = toNonEmptyWorkspaceText(language)

src/modules/app-core/workspace-sync-controller.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,16 @@ const createWorkspaceSyncController = ({
106106
return updatedTabCount
107107
}
108108

109-
const getWorkspacePrFileCommits = () => {
109+
const getWorkspacePrFileCommits = options => {
110+
const includeAllWorkspaceFiles =
111+
options?.includeAllWorkspaceFiles === true || options?.includeAll === true
110112
const snapshotTabs = buildWorkspaceTabsSnapshot()
111113
const dedupedByPath = new Map()
112114

113115
for (const tab of snapshotTabs) {
114-
const shouldCommitTab = Boolean(tab?.isDirty) || !hasTabCommittedSyncState(tab)
116+
const shouldCommitTab = includeAllWorkspaceFiles
117+
? true
118+
: Boolean(tab?.isDirty) || !hasTabCommittedSyncState(tab)
115119
if (!shouldCommitTab) {
116120
continue
117121
}
@@ -235,8 +239,15 @@ const createWorkspaceSyncController = ({
235239
repo: context.repositoryFullName || '',
236240
base: context.baseBranch || '',
237241
head: context.headBranch || '',
238-
prNumber: null,
242+
prNumber:
243+
typeof context.prNumber === 'number' && Number.isFinite(context.prNumber)
244+
? context.prNumber
245+
: null,
239246
prTitle: context.prTitle || '',
247+
prContextState:
248+
typeof context.prContextState === 'string' && context.prContextState.trim()
249+
? context.prContextState.trim()
250+
: 'inactive',
240251
renderMode: normalizeRenderMode(getRenderModeValue()),
241252
tabs: buildWorkspaceTabsSnapshot(),
242253
activeTabId: workspaceTabsState.getActiveTabId(),

src/modules/app-core/workspace-tab-mutations-controller.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const createWorkspaceTabMutationsController = ({
2727
setActiveWorkspaceTab,
2828
makeUniqueTabPath,
2929
createWorkspaceTabId,
30+
getShouldShowEditedDesign,
3031
}) => {
3132
const beginWorkspaceTabRename = tabId => {
3233
setWorkspaceTabAddMenuOpen(false)
@@ -162,6 +163,10 @@ const createWorkspaceTabMutationsController = ({
162163
const path = makeUniqueTabPath({ basePath })
163164
const tabId = createWorkspaceTabId(normalizedKind === 'styles' ? 'style' : 'module')
164165
const name = getPathFileName(path) || `${normalizedKind}-tab`
166+
const shouldMarkNewTabEdited =
167+
typeof getShouldShowEditedDesign === 'function'
168+
? Boolean(getShouldShowEditedDesign())
169+
: false
165170

166171
persistActiveTabEditorContent()
167172

@@ -173,6 +178,7 @@ const createWorkspaceTabMutationsController = ({
173178
role: 'module',
174179
isActive: false,
175180
content: '',
181+
isDirty: shouldMarkNewTabEdited,
176182
lastModified: Date.now(),
177183
})
178184

0 commit comments

Comments
 (0)