Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
137368 stories
·
31 followers

Rejoining the Windows team in 2025

1 Share
As Microsoft turns 50 and I find myself starting a new position on the Windows team after 5 years in Azure land it seems fitting, if not a little self indulgent, to reminisce about previous experiences working here. Windows 8 era My first experience in Windows came in 2011 when I moved to the DWM/Compositor team (known as presentation and composition), from the Microsoft Expression team; t...
Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Sharing Your Details Quickly At A Conference

1 Share

When you are at a tech meetup or conference, it is always awkward when you want to connect with another attendee.
I am referring here to the professional networking site Linkedin

Say you have just met someone cool at an event, and you want to connect with them professionally, and you ask them "can we connect on Linkedin", but first you have to:

  • be able to hear each other well enough
  • be able to understand each other clearly
  • know how to spell names accurately
  • The name you know them as is not necessarily the name they have listed themselves by

This is all on top of the awkwardness of asking them in the first place. I suppose you can write the name down, or swap Business Cards (do we still do that?).

So while I was listening to the latest episode of The Sam & Ray Podcast, Sam mentioned that he likes to use the Linkedin QR scanner!

Now this made my ears perk up! "What scanner?" I asked to the empty room (and if you are reading this still, you probably asked the same thing, although you may be in a coffee shop instead of my lounge).

So time to expose this lesser known feature and how you access it!

What is it?

Each registered user of Linkedin, has an automatically generated QR code (Unfortunately not a Lego QR Code) you can access quickly using the app (iPhone and Android).

How do I find it?

  • Open the LinkedIn app.
  • Click on the search bar at the top of the screen.
  • Then tap the "QR Code" icon to the right of the search bar.
  • You will now be shown your QR code (in the "My code" tab) which you can show to others to scan or you can share it or download it.

Linked in main screen with search shown

Linked in search box with qr code icon shown

My Linked in QR code screen

How do I use it?

But wait, there is another handy option, you can scan someones QR code in the app too!

  • Open the LinkedIn app.
  • Click on the search bar at the top of the screen.
  • Then tap the "QR Code" icon to the right of the search bar.
  • Tap the "Scan" tab, and your camera will open allowing you to scan another persons QR code and it will take you directly to their Linkedin profile.

Linked in main screen with search shown

Linked in search box with qr code icon shown

Linked in scan a QR Code screen

Official documentation

The official documentation is here

So if you find yourself talking to me at either {dev.talk} or re:connect conference, you will know how to connect to me on Linkedin quickly!

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Deploying a Nuxt Static Website on Azure with Pulumi

1 Share
The other day, to learn and experiment with GitHub Copilot Agent mode, I built a small website in Nuxt that provides an interactive calendar view of developer conferences around the world. I was happy with the result and I wanted to deploy it on Azure. So I created a Pulumi program in TypeScript to provision an Azure Static Web App, configure a custom domain, and retrieve a deployment token I could use to deploy the website in my GitHub Actions pipeline.

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Hosting a DigitalOcean App Platform app on a custom subdomain (with CORS)

1 Share

TL;DR

I host my blog using GitHub Pages (repo here), and have the domain registered through GoDaddy. I wanted to experiment with adding some additional functionality to my static content, using DigitalOcean App Platform (where I can essentially throw a Docker container and have it appear on the internet).

I wanted this DigitalOcean-hosted app to be available through a productiverage.com subdomain, and I wanted it to be accessible as an API from JavaScript on the page. SSL* has long been a given, and I hoped that I would hit few (if any) snags with that.

There are instructions out there for doing what I wanted, but I still encountered so many confusions and gotchas, that I figured I'd try to document the process (along with a few ways to reassure yourself when things look bleak).. even if it's only for future-me!

* (Insert pedantic comment about how TLS has replaced SSL, and so we shouldn't refer to "SSL" or "SSL certificates" - for the rest of the post, I'll be saying "SSL" and hopefully that doesn't upset anyone too much despite it not being technically correct!)

DigitalOcean App Platform

So you have something deployed using DigitalOcean's App Platform solution. It will have an automatically generated unique url that you can access it on, that is a subdomain of "ondigitalocean.app" (something like. https://productiverage-search-58yr4.ondigitalocean.app). This will not change (unless you delete your app), and you can always use it to test your application.

You want to host the application on a subdomain of a domain that you own (hosted by GoDaddy, in my case).

To start the process, go into the application's details in DigitalOcean (the initial tab you should see if called "Overview") and click into the "Settings" tab.

Note: Do not click into the "Networking" section through the link in the left hand navigation bar (under "Manage), and then into "Domains" (some guides that I found online suggested this, and it only resulted in me getting lost and confused - see the section below as to why).

This tab has the heading "App Settings" and the second section should be "Domains", click "Edit" and then the "+Add Domain" button.

Here, enter the subdomain that you want to use for your application. Again, the auto-assigned ondigitalocean.app subdomain will never go away, and you can add multiple custom domains if you want (though I only needed a single one).

You don't actually have to own the domain at this point; DigitalOcean won't do any checks other than ensuring that you don't enter a domain that is registered by something else within DigitalOcean (either one of your own resources, or a resource owned by another DigitalOcean customer). If you really wanted to, you could enter a subdomain of a domain that you know that you can't own, like "myawesomeexperiment.google.com" - but it wouldn't make a lot of sense to do this, since you would never be able to connect that subdomain to your app!

In my case, I wanted to use "search.productiverage.com".

Note: It's only the domain or subdomain that you have to enter here, not the protocol ("http" or "https") because (thankfully) it's not really an option to operate without https these days. Back in the dim and distant past, SSL certificates were frustrating to purchase, and register, and renew - and they weren't free! Today, life is a lot easier, and DigitalOcean handles it for you automatically when you use a custom subdomain on your application; they register the certificate, and automatically renew it. When you have everything working, you can look up the SSL certificate of the subdomain to confirm this - eg. when I use sslshopper.com to look up productiverage.com then I see that the details include "Server Type: GitHub.com" (same if I look up "www.productiverage.com") because I have my domain configured to point at GitHub Pages, and they look after that SSL certificate. But if I use sslshopper.com to look up search.productiverage.com then I see "Server Type: cloudflare" (although it doesn't mention DigitalOcean, it's clearly a different certificate).

With your sub/domain entered (and with DigitalOcean having checked that it's of a valid form, and not already in use by another resource), you will be asked to select some DNS management options. Click "You manage your domain" and then the "Add Domain" button at the bottom of the page.

This will redeploy your app. After which, you should see the new domain listed in the table that opened after clicked "Edit" alongside "Domains" in the "Settings" tab of your app. It will probably show the status as "Pending". It might show the status as "Configuring" at this point - if it doesn't, then refreshing the page and clicking "Edit" again alongside the "Domains" section should result in it now showing "Configuring".#

There will be a "?" icon alongside the "Configuring" status - if you hover over it you will see the message "Your domain is not yet active because the CNAME record was not found". Once we do some work on the domain registrar side (eg. GoDaddy), this status will change!

DigitalOcean App Platform - Avoiding "Networking" / "Domains"

I read some explanations of this process that said that you should configure your custom domain not by starting with the app settings, but by clicking the "Networking" link in the left hand nav (under "Manage") and then clicking into "Domains". I spent an embarrassing amount of time going down this route, and getting frustrated when I reached a step that would say something like "using the dropdown in the 'Directs to' column, select where the custom domain should be used" - I never had a dropdown, and couldn't find an explanation why!

When you configure a custom sub/domain this way, it can only be connected to (iirc) Load Balancers (which "let you distribute traffic between multiple Droplets either regionally or globally") or, I think, Reserved IPs (which you can associate with any individual Droplet, or with a DigitalOcean's managed Kubernetes service - referred to as "DOKS"). You can not select an App Platform instance in a 'Directs To' dropdown in the "Networking" / "Domains" section, and that is what was causing me to stumble since I only have my single App Platform instance right now (I don't have a load balancer or any other, more complicated infrastructure).

Final note on this; if you configure a custom domain as I'm describing, you won't see that custom domain shown in the "Networking" / "Domains" list. That is nothing to worry about - everything will still work!

My use of GoDaddy (in short; I configure DNS to serve GitHub Pages content)

Long ago, I registered my domain with GoDaddy and hosted my blog with them as an ASP.NET site. I wasn't happy with the performance of it - it was fast much of the time, but would intermittently serve requests very slowly. I had a friend who had purchased a load of hosting capacity somewhere, so I shifted my site over to that (where it was still hosted as an ASP.NET site) and configured GoDaddy to send requests that way.

Back in 2016, I shifted over to serving the blog through GitHub Pages as static content. The biggest stumbling block to this would have been the site search functionality, which I had written for my ASP.NET app in C# - but I had put together a way to push that all to JS in the client in 2013 when I got excited about Neocities being released (I'm of an age where I remember the often-hideous, but easy-to-build-and-experiment-with, Geocities pages.. back before the default approaches to publishing content seemed to within walled gardens or on pay-to-access platforms).

As my blog is on GitHub Page, I have A records configured in the DNS settings for my domain within GoDaddy that point to GitHub servers, and a CNAME record that points "www" to my GitHub subdomain "productiverage.github.io".

The GitHub documentation page "Managing a custom domain for your GitHub Pages site" describes the steps that I followed to end up in this position - see the section "Configuring an apex domain and the www subdomain variant". The redirect from "productiverage.com" to "www.productiverage.com" is managed by GitHub, as is the SSL certificate, and the redirection from "http" to "https".

Until I created my DigitalOcean app, GoDaddy's only role was to ensure that when someone tried to visit my blog that the DNS lookup resulted in them going to GitHub, who would pick up the request and serve my content.

Configuring the subdomain for DigitalOcean in GoDaddy

Within the GoDaddy "cPanel" (ie. their control panel), click into your domain, then into the "DNS" tab, and then click the "Add New Record" button. Select CNAME in the "Type" dropdown, type the subdomain segment into the "Name" text (in my case, I want DigitalOcean to use the subdomain "search.productiverage.com" so I entered "search" into that textbox, since I was managing my domain "productiverage.com"), paste the DigitalOcean-generated domain into the "Value" textbox ("productiverage-search-58yr4.ondigitalocean.app" for my app), and click "Save".

You should see a message informing you that DNS changes may take up to 48 hours to propagate, but that it usually all happens in less than an hour.

In my experience, it often only takes a few minutes for everything to work.

If you want to get an idea about how things are progressing, there are a couple of things you can do -

  1. If you open a command prompt and ping the DigitalOcean-generated subdomain (eg. "productiverage-search-58yr4.ondigitalocean.app") and then ping your new subdomain (eg. "search.productiverage.com") they should resolve to the same IP address
  2. With the IP address resolving correctly, you can try visiting the subdomain in a browser - if you get an error message like "Can't Establish a Secure Connection" then DigitalOcean hasn't finished configuring the SSL certificate, but this error is still an indicator that the DNS change has been applied (which is good news!)
  3. If you go back to your app in the DigitalOcean control panel, and refresh the "Settings" tab, and click "Edit" alongside the "Domains" section, the status will have changed from "Configuring" to "Active" when it's ready (you may have to refresh a couple of times, depending upon how patient you're being, how slow the internet is being, and whether DigitalOcean's UI automatically updates itself or not)

If you don't want to mess about with these steps, you are free to go and make a cup of tea, and everything should sort itself out on its own!

I had gone round and round so many times trying to make it work that I was desperate to have some additional insight into whether it was working or not, but now that I'm confident in the process I would probably just wait five minutes if I did this again, and jump straight to the final step..

At this point, you should be able to hit your DigitalOcean app in the browser! Hurrah!

  • If it fails, then it's worth checking that the app is still running and working when you access it via the DigitalOcean-generated address
  • If the app works at the DigitalOcean-generated address but still doesn't work on your custom subdomain, hopefully running again through those three steps above will help you identify where the blocker is, or maybe you'll find clues in the app logs in DigitalOcean

Bonus material: Enabling CORS access for the app (in DigitalOcean)

Depending upon your needs, you may be done by this point.

After I'd finished whooping triumphantly, however, I realised that I wasn't done..

My app exposes a html form that will perform a semantic search across my blog content (it's essentially my blog's Semantic Search Demo project, except that - depending upon when you read this post and when I update that code - it uses a smaller embedding model and it adds a call to a Cohere Reranker to better remove poor matches from the result set). That html form works fine in isolation.

However, the app also supports application/json requests, because I wanted to improve my blog's search by incorporating semantic search results into my existing lexical search. This meant that I would be calling the app from JS on my blog. And that would be a problem, because trying to call https://search.productiverage.com from JS code executed within the context of https://www.productiverage.com would be rejected due to the "Cross-Origin Resource Sharing" (CORS) mechanism, which exists for security purposes - essentially, to ensure that potentially-malicious JS can't send content from a site to another domain (even if the sites are on subdomains of the same domain).

To make a request through JS within the context of one domain (eg. "www.productiverage.com") to another (eg. "search.productiverage.com"), the second domain must be explicitly configured to allow access from the first. This configuration is done against the DigitalOcean app -

  1. In the DigitalOcean control panel, navigate back to the "Settings" tab for your app
  2. The first line (under the tab navigation and above the title "App Settings") should display "App" on the left and "Components" on the right - you need to click into the component (I only have a single component in my case)

DigitalOcean 'component' selection within an app

  1. Click "Edit" in the "HTTP Request Routes" section and click "Configure CORS" by the route that you will need to request from another domain (again, I only have a single route, which is for the root of my application)
  2. I want to provide access to my app only from my blog, so I set a value for the Access-Control-Allow-Origins header, that has a "Match Type" of "Exact" and an "Origin" of "https://www.productiverage.com"
  3. Click "Apply CORS" - and you should be done!

Now, you should be able to access your DigitalOcean app on the custom subdomain from another domain through JS code, without the browser giving you an error about CORS restrictions denying your attempt!

To see an example of this in action, you can go to www.productiverage.com, open the dev tools in your browser, go to the "Network" tab and filter requests to "Fetch/XHR", type something into the "Site Search" text box on the site and click "Search", and you should see requests for content SearchIndex-{something}.lz.txt (which is used for lexical searching) and a single request that looks like ?q={what you searched for} which (if you view the Headers for) you should see comes from search.productiverage.com. Woo, success!!

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

AI on Windows: Chat Styling

1 Share

In the past three posts about working with AI on Windows, we covered connecting to Phi Silica, getting progress response, and building a chat experience. In this post we’re going to improve the styling of the chat by using data templates and including a system message to setup the chat interaction.

At the end of the previous post, the layout was relatively basic, with the formatting of each item in the ListView being the ToString representation of the PhiMessage object.

To make it easier to distinguish between User input and the Assistant response, we’ll use two data templates that simply align the text to the left, for the Assistant, or the right, for the User. This will use a data template selector to pick which template to use based on the User property on the PhiMessage.

In the following video we create an ItemTemplate for the ListView, which will show the User and Message for the PhiMessage. We’ll also create a HeaderTemplate that will be data bound to a SystemMessage. For this, we’ll update the MainViewModel to include the SytemMessage property, which will be combined with the message history in forming the Phi Silica prompt.

[ObservableProperty]
private string _systemMessage = "You are an assistant for a 5 year old child";

public ObservableCollection<PhiMessage> Messages { get; } = new();

[ObservableProperty]
private string _response = string.Empty;

[RelayCommand]
public async Task SendMessage(string message)
{
    Messages.Add(new PhiMessage("User", message));
#if WINDOWS

    if (!LanguageModel.IsAvailable())
    {
        var op = await LanguageModel.MakeAvailableAsync();
    }

    var sysMessage = new PhiMessage("System", SystemMessage);

    var prompt = string.Join(Environment.NewLine, [sysMessage.ToString(), ..(from m in Messages
                                                    select m.ToString())]);


    using LanguageModel languageModel = await LanguageModel.CreateAsync();
    var progressTask = languageModel.GenerateResponseWithProgressAsync(prompt);
    progressTask.Progress +=
        (s, progress) => Dispatch(() => Response += progress);

    var result = await progressTask;
    Response = result.Response;
#else
    Response = "Design-time response....";
    await Task.Delay(1000);
    Response = "Design-time response....(updating)";
    await Task.Delay(1000);
    Response = "Final design-time response";
#endif
    Messages.Add(new PhiMessage("Assistant", Response));
    Response = string.Empty;
}

And now the video for creating the ItemTemplate and HeaderTemplate.

Now that we have an ItemTemplate, we can duplicate the template for the User template where the text is right aligned. In order to use these templates we’ll need a template selector.

public class MessageTypeSelector : DataTemplateSelector
{
    public DataTemplate UserTemplate { get; set; }

    public DataTemplate AssistantTemplate { get; set; }

    public DataTemplate SystemTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item is PhiMessage message)
        {
            return message.User switch
            {
                "System" => SystemTemplate,
                "Assistant" => AssistantTemplate,
                _ => UserTemplate,
            };
        }
        return base.SelectTemplateCore(item, container);
    }
}

The two templates and an instance of the template selector, which references the two templates, need to be added to the Resources of the page.

<Page.Resources>
    <DataTemplate x:Key="UserDataTemplate">
        <Grid>
            <StackPanel>
                <TextBlock Style="{StaticResource CaptionTextBlockStyle}"
                            Text="{Binding User}"
                            TextAlignment="Right" />
                <TextBlock Style="{StaticResource BaseTextBlockStyle}"
                            Text="{Binding Message}"
                            TextAlignment="Right" />
            </StackPanel>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="AssistanctDataTemplate">
        <Grid>
            <StackPanel>
                <TextBlock Style="{StaticResource CaptionTextBlockStyle}"
                            Text="{Binding User}" />
                <TextBlock Style="{StaticResource BaseTextBlockStyle}"
                            Text="{Binding Message}" />
            </StackPanel>
        </Grid>
    </DataTemplate>

    <local:MessageTypeSelector x:Key="MessageTypeSelector"
                                UserTemplate="{StaticResource UserDataTemplate}"
                                AssistantTemplate="{StaticResource AssistanctDataTemplate}" />
</Page.Resources>

Using Hot Design, we can update the ListView to remove the locally defined ItemTemplate and select the MessageTypeSelector resource.

Now when we run the application on Window, we can customize the system message in order to change how Phi Silica responds.

The post AI on Windows: Chat Styling appeared first on Nick's .NET Travels.

Read the whole story
alvinashcraft
6 hours ago
reply
Pennsylvania, USA
Share this story
Delete

‘Millions’ may have protested Trump and Musk yesterday

1 Share

Hundreds of thousands of people signed up to attend over 1,300 “Hands Off!” protests against President Donald Trump and Elon Musk yesterday. Today, estimates from groups involved in planning the protests suggest the protesters in the US and abroad may have actually numbered in the millions.

Activist group MoveOn is “estimating millions of attendees” went to the 1,300-plus scheduled events, with more than 100,000 turning out for the Washington, DC protest, Britt Jacovich, the group’s communications director, told The Verge via email. A press release published on the official Hands Off! website yesterday tells the same story:

Millions of people flooded the streets today at over 1,300 “Hands Off!” peaceful protests across all 50 states, U.S. territories, and a dozen locations globally, demanding an end to the authoritarian overreach by Trump and Musk.

The protests were laser-focused on Musk and Trump, but the concerns that drove yesterday’s demonstrations are wide-ranging, covering everything from Trump’s trade war and DOGE’s relentless federal agency cuts and layoffs, to LGBTQ+ and other civil rights issues, to the war in Ukraine. More than 150 groups participated in their organization, including those mentioned in this story, as well as the American Civil Liberties Union, the League of Women Voters, and labor unions like the AFL-CIO and those representing federal workers, such as the National Treasury Employees Union.

Indivisible, another of the more than 150 organizations involved in planning the protests, gives a similar estimate to MoveOn’s in a statement reported by Common Dreams, in which it says that “at virtually every single event the crowds eclipsed our estimates.” From Common Dreams:

“This is the largest day of protest since Trump retook office,” the group added. “And in many small towns and cities, activists are reporting the biggest protests their communities have ever seen as everyday people send a clear, unmistakable message to Trump and Musk: Hands off our healthcare, hands off our civil rights, hands off our schools, our freedoms, and our democracy.”

Other reported estimates from yesterday are smaller. The Guardian, The Hill, and Al Jazeera each put the number in the hundreds of thousands. Even so, millions doesn’t seem implausible. According to Axios, over 45,000 people gathered in Raleigh, North Carolina, and the outlet reports more than 100,000 people demonstrated both in Washington, DC and New York City. Organizers say more than 30,000 showed up in Chicago, writes WBEZ Chicago.

We’re building a #PeoplesMovement. Today, over 3 million people across the country stood up to say HANDS OFF our democracy.
And history shows that when just 3.5% of the population engages in sustained, peaceful resistance—transformative change is inevitable.#50501movement #HandsOff #April5

50501: The People’s Movement (@50501movement.bsky.social) 2025-04-06T00:00:04.412Z

One of the most specific numbers reported so far comes from the social media accounts of 50501, one of the most prominent protest movements that have sprung up in the wake of Musk’s actions as the head of the Department of Government Efficiency (DOGE). The group posted late yesterday that “over 3 million people across the country stood up to say HANDS OFF our democracy.”

Read the whole story
alvinashcraft
7 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories