Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a113447
build(deps): bump the rust-host group with 3 updates
dependabot[bot] Oct 14, 2026
5111721
build(deps): bump toml in /src/editors/zed in the rust-zed group
dependabot[bot] Oct 14, 2026
5950d3f
build(deps): clobber-merge dependabot/cargo/src/editors/zed/dependabo…
github-actions[bot] Oct 14, 2026
70e2a3b
build(deps): bump the github-actions group with 6 updates
dependabot[bot] Oct 14, 2026
114737a
build(deps): bump @playwright/test in /src/website in the website group
dependabot[bot] Oct 14, 2026
fe12a1a
build(deps): clobber-merge dependabot/github_actions/dependabot-upgra…
github-actions[bot] Oct 14, 2026
e98ea93
build(deps): clobber-merge dependabot/npm_and_yarn/src/website/depend…
github-actions[bot] Oct 14, 2026
c42ca83
build(deps-dev): bump the vscode-extension group
dependabot[bot] Oct 14, 2026
9929b5f
build(deps): clobber-merge dependabot/npm_and_yarn/src/editors/vscode…
github-actions[bot] Oct 14, 2026
92573de
Bump the dotnet-sidecars group with 8 updates
dependabot[bot] Oct 14, 2026
62966f6
build(deps): clobber-merge dependabot/nuget/dot-config/dotnet/dependa…
github-actions[bot] Oct 14, 2026
c63e332
Fix the open v0.22.0 review findings: parenthesized C# project names,…
MelbourneDeveloper Oct 18, 2026
238067d
CI: fail a Dependabot PR to main, whose skipped pipeline ran nothing
MelbourneDeveloper Oct 18, 2026
fc09827
fixes
MelbourneDeveloper Oct 18, 2026
fd991de
fixes
MelbourneDeveloper Oct 18, 2026
4739586
Pin lspkit at the F# profiler fixes: busy threads only, arrays never …
MelbourneDeveloper Oct 18, 2026
da6911d
Pin lspkit at main's F# profiler fixes (Nimblesite/lspkit#12): busy t…
MelbourneDeveloper Oct 18, 2026
70db4f2
fixes
MelbourneDeveloper Oct 18, 2026
fb3a882
Pin lspkit at the objects-summary and FSharp.Core anchor fixes
MelbourneDeveloper Oct 18, 2026
b2f8948
Format the profiler test kits and drop non-null assertions their asse…
MelbourneDeveloper Oct 18, 2026
211b3a9
Share the branded-panel and dashboard-snapshot test helpers
MelbourneDeveloper Oct 18, 2026
2ec7b5f
Format the F# soak suites and the process-explorer test with Prettier
MelbourneDeveloper Oct 18, 2026
7070396
Land the F# profiler suites' final shape, under 500 lines a file
MelbourneDeveloper Oct 18, 2026
4c29e9c
Pin lspkit at main's F# memory fixes (Nimblesite/lspkit#13, #14)
MelbourneDeveloper Oct 18, 2026
c4f4d27
Refuse a profiler command whose arguments are not one params object
MelbourneDeveloper Oct 18, 2026
4da2a5a
Profile F# in the docs with a narrated walkthrough, and make the film…
MelbourneDeveloper Oct 18, 2026
75b75b5
Pin lspkit at main's profiler fixes (Nimblesite/lspkit#15)
MelbourneDeveloper Oct 18, 2026
d8ccb5e
Tighten the F# profiler suites around the fixed sessions and summaries
MelbourneDeveloper Oct 18, 2026
4e48725
Open the F# CPU truth test's Stopwatch window once samples flow
MelbourneDeveloper Oct 18, 2026
3c7cfe4
Record the F# profiler test bed and the defects it found in the profi…
MelbourneDeveloper Oct 18, 2026
31f3770
Send cpu.list its params object from the profiler suites that called …
MelbourneDeveloper Oct 18, 2026
e11cc0f
Pin lspkit at main's progress log fix (Nimblesite/lspkit#16)
MelbourneDeveloper Oct 18, 2026
87f2da7
Run a C# project's source generators when F# compiles it in memory
MelbourneDeveloper Oct 18, 2026
d03507f
Restore a real repository without freezing the extension host
MelbourneDeveloper Oct 18, 2026
a8971c9
Run source generators without byrefs, and never let one fail a load
MelbourneDeveloper Oct 18, 2026
e85a391
Narrate a film for every docs page, and fix the bugs filming found
MelbourneDeveloper Oct 18, 2026
1dbc6bf
Docs stills from the film kit; retire the old captures that showed ho…
MelbourneDeveloper Oct 18, 2026
6a1e009
Fix website video playback in Windows Chrome and Edge
MelbourneDeveloper Oct 19, 2026
ee096d9
fixes
MelbourneDeveloper Oct 19, 2026
666c942
fixes
MelbourneDeveloper Oct 19, 2026
fb348b5
Merge branch 'feature/narrated-doc-films' into fix/windows-video-play…
MelbourneDeveloper Oct 19, 2026
91614f2
Merge branch 'fix/382-netfx-review-findings' into fix/windows-video-p…
MelbourneDeveloper Oct 19, 2026
5ad3288
Merge branch 'feature/doc-stills' into fix/windows-video-playback
MelbourneDeveloper Oct 19, 2026
501bec0
Merge remote-tracking branch 'origin/dependabot-upgrades' into fix/wi…
MelbourneDeveloper Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/specs/DESIGN-SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Screenshots and video of a SharpLsp feature show the real extension, never a moc
- **Privacy.** No frame may show a user name, home directory or hostname. The demo runs from a neutral temporary path, and the editor runs on a copy-on-write clone of the .NET install under that path, with its login-shell environment off, so every path it prints is neutral. A terminal appears only where the feature runs in one (F# Interactive, a build task, a contributor's command line), and a shell terminal starts with no profile and a bare `$ ` prompt.
- **Formats.**
- Films ship as H.264 MP4 and VP9 WebM with a WebP poster.
- Prefer H.264 MP4 before WebM: Windows Chrome/Edge may accept VP9 but fail during decoding, without trying the next source. Playback tests must verify advancing time and decoded frames, pause, seek and resume on installed Windows Chrome and Edge as well as desktop and phone browser projects.
- Stills ship as WebP, 2400px wide, with `width` and `height` set.
- Marketing pages may autoplay a film only when it is muted and looping; docs pages use controls and are never muted.
- Every autoplaying video pauses and shows its controls under `prefers-reduced-motion: reduce`.
Expand Down
15 changes: 13 additions & 2 deletions src/website/playwright.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { defineConfig, devices } from '@playwright/test';

// [WEB-DESIGN-MEDIA] Windows WebKit rejects known-good H.264. Keep phone viewport/touch coverage
// on installed Chrome there, and WebKit elsewhere, without skipping decoding.
const phoneBrowser = process.platform === 'win32' ? { browserName: 'chromium', channel: 'chrome' } : {};

export default defineConfig({
testDir: './tests',
fullyParallel: true,
Expand All @@ -12,17 +16,24 @@ export default defineConfig({
trace: 'on-first-retry',
},
projects: [
// The Windows system decoder failed where bundled Chromium/WebKit passed.
// Keep the regression on installed browsers, with their default GPU settings.
...(process.platform === 'win32' ? ['chrome', 'msedge'].map((channel) => ({
name: channel,
testMatch: 'video-playback.spec.js',
use: { ...devices['Desktop Chrome'], channel },
})) : []),
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'iPhone SE',
use: { ...devices['iPhone SE'] },
use: { ...devices['iPhone SE'], ...phoneBrowser },
},
{
name: 'iPhone 14',
use: { ...devices['iPhone 14'] },
use: { ...devices['iPhone 14'], ...phoneBrowser },
},
],
webServer: {
Expand Down
36 changes: 18 additions & 18 deletions src/website/src/_data/videos.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"profiler-film": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1080,
"duration": "PT1M4S",
Expand Down Expand Up @@ -63,7 +63,7 @@
}
},
"profiler-fix-memory-leak": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M32S",
Expand All @@ -85,7 +85,7 @@
"captions": ["en", "ja", "zh"]
},
"profiler-fsharp": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M52S",
Expand All @@ -107,7 +107,7 @@
}
},
"sharplsp-tour": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1000,
"height": 562,
"duration": "PT22S",
Expand All @@ -128,7 +128,7 @@
}
},
"getting-started": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M4S",
Expand All @@ -150,7 +150,7 @@
}
},
"editor-setup": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT41S",
Expand All @@ -172,7 +172,7 @@
}
},
"configuration": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT51S",
Expand All @@ -194,7 +194,7 @@
}
},
"fsharp-language": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M53S",
Expand All @@ -216,7 +216,7 @@
}
},
"code-completions": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M9S",
Expand All @@ -238,7 +238,7 @@
}
},
"hover": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M15S",
Expand All @@ -260,7 +260,7 @@
}
},
"navigation": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M0S",
Expand All @@ -282,7 +282,7 @@
}
},
"diagnostics": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M24S",
Expand All @@ -304,7 +304,7 @@
}
},
"refactoring": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M27S",
Expand All @@ -326,7 +326,7 @@
}
},
"context-menus": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M10S",
Expand All @@ -348,7 +348,7 @@
}
},
"nuget": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT1M18S",
Expand All @@ -370,7 +370,7 @@
}
},
"profiler-csharp": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT2M16S",
Expand All @@ -392,7 +392,7 @@
}
},
"architecture": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT38S",
Expand All @@ -414,7 +414,7 @@
}
},
"contributing": {
"encodings": ["webm", "mp4"],
"encodings": ["mp4", "webm"],
"width": 1920,
"height": 1200,
"duration": "PT38S",
Expand Down
13 changes: 13 additions & 0 deletions src/website/tests/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ export async function expectPlayable(request, urls, kind) {
export const playback = (video) =>
video.evaluate((element) => ({ paused: element.paused, controls: element.controls, muted: element.muted }));

/** Real decoding, including errors raised AFTER play() resolves. [WEB-DESIGN-MEDIA] */
export async function expectFramesAdvance(video, from) {
const frames = await video.evaluate((v) => v.getVideoPlaybackQuality().totalVideoFrames);
await expect.poll(() => video.evaluate((v, start) => ({
error: v.error?.message ?? null,
paused: v.paused,
advancing: v.currentTime > start.time + 1,
decoding: v.getVideoPlaybackQuality().totalVideoFrames > start.frames + 5,
}), { time: from, frames }), { timeout: 15000 }).toEqual({
error: null, paused: false, advancing: true, decoding: true,
});
}

export const sourcesOf = (video) =>
video.locator('source').evaluateAll((sources) => sources.map((s) => s.getAttribute('src')));

Expand Down
2 changes: 1 addition & 1 deletion src/website/tests/profiler-media.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { expectPlayable, expectStills, playback, sourcesOf } from './media.js';

const FILM_POSTER = '/assets/videos/profiler-film-poster.webp';
const WALKTHROUGH_POSTER = '/assets/videos/profiler-csharp-poster.webp';
const FILM_SOURCES = ['/assets/videos/profiler-film.webm', '/assets/videos/profiler-film.mp4'];
const FILM_SOURCES = ['/assets/videos/profiler-film.mp4', '/assets/videos/profiler-film.webm'];
const PROFILER_STILLS = 8;
const DOCS = ['/docs/profiler/', '/zh/docs/profiler/', '/ja/docs/profiler/'];

Expand Down
2 changes: 1 addition & 1 deletion src/website/tests/site-videos.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function expectDescribed(object, id, label, sources) {
expect(object.name, 'search sees the same title the reader hears').toBe(label);
expect(object.description.length, 'a real description').toBeGreaterThanOrEqual(20);
expect(object.thumbnailUrl).toBe(`${SITE}${poster(id)}`);
expect(object.contentUrl).toBe(`${SITE}${sources.at(-1)}`);
expect(object.contentUrl, 'search links to the preferred playable encoding').toBe(`${SITE}${sources[0]}`);
expect(object.duration).toMatch(/^PT(\d+M)?\d+S$/);
expect(Number.isNaN(Date.parse(object.uploadDate)), 'uploadDate is a real date').toBe(false);
}
Expand Down
34 changes: 34 additions & 0 deletions src/website/tests/video-playback.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Regression for [WEB-DESIGN-MEDIA]: a served file and a resolved play()
// promise do not prove playback. Windows Chrome/Edge decoded one WebM frame,
// then paused with PIPELINE_ERROR_DECODE. Exercise the real media pipeline.
import { test, expect } from '@playwright/test';
import { expectFramesAdvance } from './media.js';

const FILMS = [
['/docs/fsharp/', 0], ['/docs/fsharp-profiling/', 0],
['/docs/', 0], ['/docs/editors/', 0], ['/docs/configuration/', 0],
['/docs/completions/', 0], ['/docs/hover/', 0], ['/docs/go-to-definition/', 0],
['/docs/diagnostics/', 0], ['/docs/refactoring/', 0], ['/docs/context-menus/', 0],
['/docs/nuget/', 0], ['/docs/profiler/', 0], ['/docs/profiler/', 1],
['/docs/profiler/', 2], ['/docs/memory-leaks/', 0],
['/docs/architecture/', 0], ['/docs/contributing/', 0],
['/blog/introducing-sharplsp/', 0], ['/blog/editor-agnostic-dotnet-lsp/', 0],
];

for (const [route, index] of FILMS) {
test(`${route} video ${index} decodes, pauses, seeks and resumes`, async ({ page }) => {
await page.goto(route);
const video = page.locator('figure video').nth(index);
await video.scrollIntoViewIfNeeded();
await video.evaluate((v) => v.play());
await expectFramesAdvance(video, 0);
await video.evaluate((v) => v.pause());
await expect(video).toHaveJSProperty('paused', true);
const stopped = await video.evaluate((v) => v.currentTime);
await page.waitForTimeout(250);
await expect(video).toHaveJSProperty('currentTime', stopped);
const seek = await video.evaluate((v) => { v.currentTime = v.duration / 2; return v.currentTime; });
await video.evaluate((v) => v.play());
await expectFramesAdvance(video, seek);
});
}
4 changes: 3 additions & 1 deletion src/website/video-figure.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// films/profiler/render.mjs). The tour is
// the Marketplace GIF (assets/images/marketplace/sharplsp-tour.gif) re-encoded
// with the same H.264/VP9 settings, because a GIF is eight times the bytes.
// MP4 comes first in the catalogue: Windows Chrome/Edge can accept the VP9
// source then fail decoding it, without falling back to the next source.
import markdownIt from "markdown-it";
import languages from "./src/_data/languages.json" with { type: "json" };
import site from "./src/_data/site.json" with { type: "json" };
Expand Down Expand Up @@ -58,7 +60,7 @@ function structuredData(id, video, text) {
name: text.label,
description: text.caption,
thumbnailUrl: `${site.url}${asset(id, "-poster.webp")}`,
contentUrl: `${site.url}${asset(id, `.${video.encodings.at(-1)}`)}`,
contentUrl: `${site.url}${asset(id, `.${video.encodings[0]}`)}`,
uploadDate: video.uploadDate,
duration: video.duration,
width: video.width,
Expand Down
Loading