Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
•••••••
Sichere ProgrammierungNaza, an O-Level tutor for WAEC, NECO, and JAMB(01.10.2026 um 02:08 Uhr)
••••••••••
Sichere ProgrammierungNaza, an O-Level tutor for WAEC, NECO, and JAMB(01.10.2026 um 02:08 Uhr)
•••
Intelligence View
⚡ tsecurity.de Intelligence

DotShare v3.0 — I Built a Full Publishing Suite Inside VS Code (And What I Learned)

How I went from a simple social media poster to a full publishing suite with Dev.to, Medium, thread composers, and auth health checks — all without leaving VS C…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!




How I went from a simple social media poster to a full publishing suite with Dev.to, Medium, thread composers, and auth health checks — all without leaving VS Code.



I've been building DotShare — a VS Code extension that lets you post to LinkedIn, X, Bluesky, Telegram, Reddit, Discord, and Facebook directly from your editor. After shipping v2.4 with one-click OAuth, I thought I was done for a while.



Then I started writing more long-form content. Every time I finished a markdown article in VS Code, I had to open a browser, go to Dev.to, copy-paste my content, fix the formatting, add tags, set the canonical URL... you know the drill.



So I rebuilt the whole thing.









What Changed in v3.0 — The Publishing Suite



The headline feature is blog publishing — but the real story is the architecture behind it.






1. Platform-First Navigation



The old UI had "workspace tabs" — you picked Threads, Social, or Blogs, then selected your platform. It was confusing and redundant.



In v3.0, I flipped it: click the platform icon, the workspace adapts automatically.




// platform-config.ts — single source of truth
export const PLATFORM_CONFIGS: Record<string, PlatformConfig> = {
x: {
name: 'X / Twitter',
icon: '𝕏',
maxChars: 280,
premiumMaxChars: 25000,
supportsThreads: true,
workspaceType: 'threads', // → Thread Composer
charCountMethod: 'twitter',
authType: 'oauth2',
},
linkedin: {
name: 'LinkedIn',
maxChars: 3000,
supportsThreads: false,
workspaceType: 'social', // → Social Composer
authType: 'oauth2',
},
devto: {
name: 'Dev.to',
maxChars: 100000,
workspaceType: 'blogs', // → Article Publisher
authType: 'api_key',
},
};






This file is shared between the extension host and the WebView — no VS Code imports, just pure TypeScript. When the user clicks the Dev.to icon, switchPlatform('devto') reads workspaceType: 'blogs' and shows the Article Publisher. One source of truth, zero hardcoded workspace routing.






2. Dev.to Integration



Dev.to has a clean REST API. You POST to /api/articles with your api-key header and you're done.




// devto.ts
export async function shareToDevTo(
apiKey: string,
articleData: {
text: string;
title?: string;
tags?: string[];
description?: string;
coverImage?: string;
published?: boolean;
canonicalUrl?: string;
series?: string;
}
): Promise<string> {
const { title, body } = extractTitleFromMarkdown(articleData.text);
const tags = articleData.tags?.length
? articleData.tags.slice(0, 4) // Dev.to max: 4 tags
: extractTags(articleData.text);

const payload = {
article: {
title: articleData.title || title,
body_markdown: body,
tags,
published: articleData.published ?? true,
...(articleData.coverImage && { main_image: articleData.coverImage }),
...(articleData.canonicalUrl && { canonical_url: articleData.canonicalUrl }),
...(articleData.series?.trim() && { series: articleData.series.trim() }),
},
};

const response = await axios.post(
'https://dev.to/api/articles',
payload,
{ headers: { 'api-key': apiKey, 'Content-Type': 'application/json' } }
);

return response.data.url;
}






One thing I learned the hard way: Dev.to does not support image file uploads via API. The dev-to-uploads.s3.amazonaws.com bucket you see in articles is only accessible through their web editor. The API only accepts public URLs. So if you're building an integration, skip the upload logic entirely and tell users to host images externally.






3. YAML Frontmatter Parser



When you click "Load Current File", DotShare reads the active .md file from VS Code's editor and parses the frontmatter:




// frontmatter-parser.ts
export function parseFrontMatter(content: string): {
hasFrontmatter: boolean;
frontmatter?: FrontMatter;
body: string;
} {
const FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
const match = content.match(FRONTMATTER_REGEX);

if (!match) return { hasFrontmatter: false, body: content };

const [, yamlContent, body] = match;
const frontmatter: FrontMatter = {};

for (const line of yamlContent.split('\n')) {
const colonIndex = line.indexOf(':');
if (colonIndex === -1) continue;

const key = line.slice(0, colonIndex).trim();
const value = line.slice(colonIndex + 1).trim();

switch (key) {
case 'title': frontmatter.title = value.replace(/^['"]|['"]$/g, ''); break;
case 'tags': frontmatter.tags = parseTags(value); break;
case 'canonical_url': frontmatter.canonical_url = value; break;
case 'description': frontmatter.description = value; break;
case 'cover_image': frontmatter.cover_image = value; break;
case 'series': frontmatter.series = value; break;
case 'published': frontmatter.published = value === 'true'; break;
}
}

return { hasFrontmatter: true, frontmatter, body: body.trim() };
}






The publish form auto-fills from whatever frontmatter exists. If your article already has title, tags, and canonical_url in the frontmatter, you open DotShare, click Load, and hit Publish. Done.



DotShare Frontmatter Parser loading a markdown file into the Dev.to publisher






4. Thread Composer + X Premium



X and Bluesky both support threads. The thread composer lets you build them post-by-post, with a per-post character counter that respects each platform's rules.



For X, I added a Premium toggle that switches the limit from 280 to 25,000 characters:




// In app.ts (WebView side)
const premiumToggle = document.getElementById('x-premium-toggle') as HTMLInputElement;
premiumToggle.addEventListener('change', () => {
const isPremium = premiumToggle.checked;
const maxChars = isPremium
? PLATFORM_CONFIGS['x'].premiumMaxChars // 25000
: PLATFORM_CONFIGS['x'].maxChars; // 280

updateAllCharCounters('x', maxChars);
});






For Bluesky threads, the extension joins all posts with a manual separator (===) and the bluesky.ts adapter splits them back:




// bluesky.ts
function createThreadChunks(text: string, maxLength = 300): string[] {
// Manual separator from Thread Composer
const manualParts = text.split(/\n\n===\n\n/);
if (manualParts.length > 1) {
const chunks: string[] = [];
for (const part of manualParts) {
const trimmed = part.trim();
if (trimmed.length > maxLength) {
chunks.push(...splitByWords(trimmed, maxLength));
} else {
chunks.push(trimmed);
}
}
return chunks.map((chunk, i) => `${chunk} (${i + 1}/${chunks.length})`);
}
return splitByWords(text, maxLength);
}









5. Twitter Character Counting — A Subtle Bug



Twitter counts every URL as exactly 23 characters (their t.co proxy length), regardless of the actual URL length. I had a bug in the original implementation:




// ❌ Wrong — short URLs were counted as their actual length
text.replace(URL_REGEX, (url) => 'x'.repeat(Math.min(url.length, 23)))

// ✅ Correct — all URLs are always 23 chars
text.replace(URL_REGEX, 'x'.repeat(23))






Small bug, but it made the character counter off for short URLs like https://t.co/abc.









v3.0.1 — The Medium Situation



Shortly after shipping v3.0, I realized something: Medium has stopped issuing new integration tokens.



Their official response from help.medium.com:




"Medium will not be issuing any new integration tokens for our API and will not allow any new integrations. All existing tokens will continue to work."




The only workaround is to email [email protected] and request access manually — they do respond within 24 hours and do activate the token section on your account. So the medium.ts integration is solid, but the onboarding requires this extra step.



Medium API Notice in DotShare settings and publishing view



I added a notice directly in the Settings card so users know before they go hunting for a token that doesn't exist in the UI:




<p class="platform-notice">
⚠️ Medium no longer issues new tokens automatically.
Email <strong>[email protected]</strong> to request access.
They usually respond within 24 hours.
</p>






The Medium API itself works fine — it's just the token provisioning that requires the manual email step.









v3.1.0 — Reliability: Auth Server Health Checks



DotShare uses an OAuth server (dotshare-auth-server.vercel.app) for X, Reddit, and Facebook. The TokenManager handles auto-refresh of expiring tokens.



The problem: if the auth server goes down, the user just gets a generic error when they try to post. The connect button stays enabled, the user clicks it, nothing happens.



The fix is straightforward — check health before attempting any OAuth operation, cache the result, and disable the button with a clear message if the server is unreachable:




// TokenManager.ts
let _lastHealthCheck: { ok: boolean; timestamp: number } | null = null;
const HEALTH_CACHE_MS = 30 * 1000; // 30 seconds

static async checkHealth(): Promise<boolean> {
// Return cached result if still fresh
if (_lastHealthCheck && Date.now() - _lastHealthCheck.timestamp < HEALTH_CACHE_MS) {
return _lastHealthCheck.ok;
}

try {
await axios.get(`${AUTH_SERVER_URL}/health`, { timeout: 5000 });
_lastHealthCheck = { ok: true, timestamp: Date.now() };
return true;
} catch {
_lastHealthCheck = { ok: false, timestamp: Date.now() };
Logger.warn('TokenManager: auth server unreachable ❌');
return false;
}
}






The 30-second cache is important — without it, every share attempt on X would make an extra HTTP request just to check if the server is alive.



In PostHandler, the OAuth platforms check health before trying to refresh:




private async shareToXWithUpdate(post: PostData, postId: string | undefined): Promise<void> {
// Only check health if user is on OAuth (has a refresh token)
const hasOAuthToken = !!(await this.context.secrets.get('xRefreshToken'));
if (hasOAuthToken) {
const serverOk = await TokenManager.checkHealth();
if (!serverOk) {
// Tell the WebView to disable the button
this.view.webview.postMessage({
command: 'oauthServerDown',
platform: 'x',
});
throw new Error('Auth server is currently unavailable. Please try again later.');
}
}

const xAccessToken = await TokenManager.getValidToken('x')
|| await this.context.secrets.get('xAccessToken') || '';

if (!xAccessToken) {
throw new Error(
'X credentials lost (possible Keychain issue). Please reconnect X in Settings.'
);
}

await shareToX(xAccessToken, '', post);
this.sendSuccess('Successfully posted to X!');
}






And in app.ts on the WebView side:




case 'oauthServerDown': {
const btn = document.getElementById(`oauthConnect_${msg.platform}`) as HTMLButtonElement;
if (btn) {
btn.disabled = true;
btn.textContent = '⚠️ Server unavailable — try later';
}
break;
}






Simple, no fallback complexity, no "switch to manual mode" dialogs. Server is down → button is disabled → clear message. The user can still use the "Advanced: Enter token manually" section if they have their own token.






Keychain Wipeout on Linux



VS Code's context.secrets is backed by the OS keychain — gnome-keyring on GNOME, libsecret on other Linux setups. Sometimes after a system update or password change, the keychain loses sync and all stored secrets come back empty.



The original error for missing X credentials was:




Error sharing to X: Error: X credentials not configured






Not helpful. Is it not configured, or did the keychain wipe it? The new message:




X credentials lost (possible Keychain issue). Please reconnect X in Settings.






This tells the user exactly what might have happened and what to do about it. Small change, big difference in support requests.









What's Next



The roadmap has v3.2 as a scheduling engine overhaul — recurring posts, a queue manager UI, time zone support, and a calendar view of all scheduled posts. After that, v3.3 is the AI co-pilot phase: platform-specific rewrites, smart thread splitting, and blog-to-social post generation.



The extension is free and open source at github.com/kareem2099/DotShare. If you write technical content and want to stop context-switching between VS Code and your browser, give it a try.






If you hit any issues or want to contribute, open an issue on GitHub. Feedback on the Dev.to integration especially — I want to make the publishing flow as smooth as possible.

2. Cyber Threat Intelligence & Forensik

IoC Intelligence (1 Indikatoren)
dev[.]to
CTI Threat Relationship Graph4 Knoten / 3 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten DotShare v3.0 — I Built a Full Publishing Suite Inside VS Code (And What I Learned)

Thematisch verwandte Begriffe: DotShare, Built, Full, Publishing · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag