Skip to content
This repository was archived by the owner on Feb 6, 2026. It is now read-only.

Commit fc35003

Browse files
authored
Merge pull request #7754 from systeminit/jobelenus/users-not-finding-changeset
Onboarding users are not finding change sets when they're created by agent / MCP
2 parents e4ed48f + 2684875 commit fc35003

5 files changed

Lines changed: 93 additions & 2 deletions

File tree

app/web/src/api/sdf/dal/change_set.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface ChangeSet {
2929
updatedAt?: IsoDateString;
3030
abandonRequestedAt?: IsoDateString;
3131
abandonRequestedByUserId?: UserId;
32+
createdByUserId?: UserId;
3233
}
3334

3435
export type ChangeStatus = "added" | "deleted" | "modified" | "unmodified";

app/web/src/newhotness/nav/ChangeSetPanel.vue

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
:enableSecondaryAction="calculateShowSecondaryAction"
2020
:sizeClass="tw`h-[28px]`"
2121
secondaryActionIcon="edit2"
22+
:hoverBorder="false"
23+
:class="youHaveNewChangeSet && 'new-change-set-alert'"
2224
@select="onSelectChangeSet"
2325
@secondaryAction="openRenameModal"
26+
@click="clearAnimation"
2427
>
2528
<template #afterOptions>
2629
<DropdownMenuItem
@@ -158,6 +161,27 @@ const ctx = useContext();
158161
159162
const { openChangeSets, changeSet } = useChangeSets(computed(() => ctx));
160163
164+
// when change set list updates with new data
165+
// are any of the new CS from me (or my AI agent?)
166+
const youHaveNewChangeSet = ref(false);
167+
watch([openChangeSets, ctx.changeSetId], ([newCS, _], [oldCS, _c]) => {
168+
const myOld = new Set(
169+
oldCS.filter((c) => c.createdByUserId === ctx.user?.pk).map((c) => c.id),
170+
);
171+
const myNew = new Set(
172+
newCS.filter((c) => c.createdByUserId === ctx.user?.pk).map((c) => c.id),
173+
);
174+
const diff = [...myNew].filter((id) => !myOld.has(id));
175+
if (diff.length > 1) youHaveNewChangeSet.value = true;
176+
else if (diff.length === 0) youHaveNewChangeSet.value = false;
177+
else if (diff[0] && diff[0] !== ctx.changeSetId.value)
178+
youHaveNewChangeSet.value = true;
179+
});
180+
181+
const clearAnimation = () => {
182+
youHaveNewChangeSet.value = false;
183+
};
184+
161185
const dropdownMenuRef = ref<InstanceType<typeof DropdownMenuButton>>();
162186
163187
const changeSetDropdownOptions = computed(() => [
@@ -297,3 +321,60 @@ function openCreateModal() {
297321
298322
defineExpose({ openCreateModal });
299323
</script>
324+
325+
<style scoped>
326+
/*
327+
Taken right from the lobby with minimal alterations (jobelenus)
328+
Note*(victor): These styles exist to power the spinning border on the lobby terminal */
329+
330+
/*
331+
We need to declare --angle as a property with an initial value so that keyframes can correctly interpolate it
332+
If we don't, the keyframes below would only blip between the declared states
333+
*/
334+
@property --angle {
335+
syntax: "<angle>";
336+
inherits: false;
337+
initial-value: 0deg;
338+
}
339+
@keyframes borderRotate {
340+
100% {
341+
--angle: 360deg;
342+
}
343+
}
344+
345+
@keyframes pulse {
346+
0%,
347+
35%,
348+
70%,
349+
100% {
350+
background-color: #000;
351+
}
352+
50% {
353+
background-color: rgba(134, 239, 172, 0.25); /* success-300 */
354+
}
355+
}
356+
357+
.new-change-set-alert {
358+
border: 1px solid;
359+
/*
360+
use a spinning conic-gradient as the border image. It looks like this: https://www.geeksforgeeks.org/css/css-conic-gradient-function/
361+
But "masked" through the border
362+
*/
363+
border-image: conic-gradient(
364+
from var(--angle),
365+
#333,
366+
#333 0.65turn,
367+
#86efac 1turn /* success-300 */
368+
)
369+
1;
370+
/*
371+
Enable the animation. Although the rotation is linear, since it's showing through a rectangular shape,
372+
both the moving speed and the trail length vary depending on the position. We could fudge the border speed by
373+
compensating on the keyframes vs the "radius" of each border position but this wouldn't fix the trail so
374+
we chose to use this as is.
375+
*/
376+
animation: borderRotate 1500ms linear infinite forwards, pulse 5s infinite;
377+
/* border-radius does not interact with border images, so this all black mask that takes the size of the div makes it round again */
378+
mask-image: radial-gradient(#000 0, #000 0);
379+
}
380+
</style>

lib/dal/src/change_set.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ pub struct ChangeSet {
275275
pub merge_requested_at: Option<DateTime<Utc>>,
276276
pub reviewed_by_user_id: Option<UserPk>,
277277
pub reviewed_at: Option<DateTime<Utc>>,
278+
pub created_by_user_id: Option<UserPk>,
278279
}
279280

280281
impl TryFrom<PgRow> for ChangeSet {
@@ -296,6 +297,7 @@ impl TryFrom<PgRow> for ChangeSet {
296297
merge_requested_at: value.try_get("merge_requested_at")?,
297298
reviewed_by_user_id: value.try_get("reviewed_by_user_id")?,
298299
reviewed_at: value.try_get("reviewed_at")?,
300+
created_by_user_id: value.try_get("created_by_user_id")?,
299301
})
300302
}
301303
}
@@ -310,15 +312,19 @@ impl ChangeSet {
310312
let id: Ulid = Ulid::new();
311313
let change_set_id: ChangeSetId = id.into();
312314
let workspace_id = ctx.tenancy().workspace_pk_opt();
315+
let user_id = match ctx.history_actor() {
316+
HistoryActor::User(user_pk) => Some(user_pk),
317+
_ => None,
318+
};
313319

314320
let name = name.as_ref();
315321
let row = ctx
316322
.txns()
317323
.await?
318324
.pg()
319325
.query_one(
320-
"INSERT INTO change_set_pointers (id, name, base_change_set_id, status, workspace_id, workspace_snapshot_address) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
321-
&[&change_set_id, &name, &base_change_set_id, &ChangeSetStatus::Open.to_string(), &workspace_id, &workspace_snapshot_address],
326+
"INSERT INTO change_set_pointers (id, name, base_change_set_id, status, workspace_id, workspace_snapshot_address, created_by_user_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *",
327+
&[&change_set_id, &name, &base_change_set_id, &ChangeSetStatus::Open.to_string(), &workspace_id, &workspace_snapshot_address, &user_id],
322328
)
323329
.await?;
324330
let change_set = Self::try_from(row)?;
@@ -391,6 +397,7 @@ impl ChangeSet {
391397
reviewed_by_user_id: self.reviewed_by_user_id.map(|id| id.into()),
392398
reviewed_by_user,
393399
reviewed_at: self.reviewed_at,
400+
created_by_user_id: self.created_by_user_id.map(|id| id.into()),
394401
};
395402

396403
Ok(change_set)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE change_set_pointers ADD COLUMN created_by_user_id ident;

lib/si-frontend-types-rs/src/change_set.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct ChangeSet {
3636
pub reviewed_by_user_id: Option<String>,
3737
pub reviewed_by_user: Option<String>,
3838
pub reviewed_at: Option<DateTime<Utc>>,
39+
pub created_by_user_id: Option<String>,
3940
}
4041

4142
#[derive(Deserialize, Serialize, Debug)]

0 commit comments

Comments
 (0)