<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>terminal &#8211; interloper.</title>
	<atom:link href="https://interloper.ie/tag/terminal/feed/" rel="self" type="application/rss+xml" />
	<link>https://interloper.ie</link>
	<description>Marcus Craig - Artist &#38; Technician</description>
	<lastBuildDate>Thu, 01 Jan 2026 16:30:26 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://interloper.ie/wp-content/uploads/2023/10/Untitled-2-150x150.png</url>
	<title>terminal &#8211; interloper.</title>
	<link>https://interloper.ie</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Terminal</title>
		<link>https://interloper.ie/terminal/</link>
		
		<dc:creator><![CDATA[root]]></dc:creator>
		<pubDate>Sat, 09 Aug 2025 19:02:00 +0000</pubDate>
				<category><![CDATA[One Day Projects]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[one-day-project]]></category>
		<category><![CDATA[operating system]]></category>
		<category><![CDATA[os]]></category>
		<category><![CDATA[terminal]]></category>
		<guid isPermaLink="false">https://www.interloper.ie/?p=702</guid>

					<description><![CDATA[Terminal OS It has been a while since I made a one-day project. Always enjoyed the idea of a web based &#8216;terminal&#8217; that mimics a SSH connection or a headless CLI. I could easily expose a MySQL demo user and designate a sandbox portion of my VM &#8211; but making it a single HTML is [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 data-wp-context---core-fit-text="core/fit-text::{&quot;fontSize&quot;:&quot;&quot;}" data-wp-init---core-fit-text="core/fit-text::callbacks.init" data-wp-interactive data-wp-style--font-size="core/fit-text::context.fontSize" class="wp-block-heading has-fit-text">Terminal OS</h2>


<div class="wp-block-post-date"><time datetime="2025-08-09T21:02:00+02:00">09/08/2025</time></div>


<p>It has been a while since I made a one-day project. Always enjoyed the idea of a web based &#8216;terminal&#8217; that mimics a SSH connection or a headless CLI. I could easily expose a MySQL demo user and designate a sandbox portion of my VM &#8211; but making it a single HTML is much more fun (and, you know, safe).<br><br>Adding a few functions that pull from some public APIs such as a weather search and some selected news headlines via RSS. Adding the ability to query the WordPress CMS so you can search Pages/Posts, all without requiring its REST API. I could always add some persistence with some PHP sessions or even just the browsers localStorage, but I prefer this to be as instanced and lightweight as possible. This is all contained within a single HTML file (&lt;20kb).<br><br>This project can be found formerly at <a href="https://interloper.ie/os">interloper.ie/os</a></p>



<!-- Terminal iframe wrapper -->
<style>
.terminal-embed{
  position:relative;
  width:100%;
  aspect-ratio:16/9;                 /* default desktop letterbox */
  overflow:hidden;
  border-radius:16px;
  box-shadow:0 2px 16px rgba(0,0,0,.08);
  background:#0b0b0b;
}

/* The iframe */
.terminal-embed iframe{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  border:0;
}

/* Fullscreen button */
.terminal-embed .fs-btn{
  position:absolute;
  top:10px;
  right:10px;
  z-index:2;                         /* ensure above iframe */
  display:inline-flex;
  align-items:center;
  gap:.45rem;
  padding:.5rem .7rem;
  border-radius:10px;
  background:rgba(10,10,10,.7);
  color:#e8e8e8;
  border:1px solid rgba(255,255,255,.15);
  backdrop-filter: blur(4px);
  cursor:pointer;
  font:500 13px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  user-select:none;

  opacity:0;
  transform: translateY(-4px);
  transition: opacity .18s ease, transform .18s ease, background .18s ease;
}
.terminal-embed .fs-btn:hover{ background:rgba(10,10,10,.85); }
.terminal-embed .fs-btn svg{ width:16px; height:16px; }

/* Fade in on hover/focus, and keep shown while fullscreen */
.terminal-embed:hover .fs-btn,
.terminal-embed:focus-within .fs-btn,
.terminal-embed.is-fullscreen .fs-btn{
  opacity:1;
  transform:none;
}

/* No hover (touch): keep visible */
@media (hover:none){
  .terminal-embed .fs-btn{ opacity:1; transform:none; }
}

/* Phones in portrait: use viewport height instead of 16:9 */
@media (max-width:700px) and (orientation:portrait){
  .terminal-embed{ aspect-ratio:auto; height:85vh; }
  .terminal-embed iframe{ height:100%; }
}
</style>

<div class="terminal-embed" id="terminal-embed" aria-label="Interactive terminal embed">
  <button class="fs-btn" type="button" aria-label="Enter fullscreen" title="Fullscreen">
    <!-- “enter fullscreen” icon -->
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path fill="currentColor"
        d="M7 3h2v4h4v2H7V3zm8 0h2v6h-6V7h4V3zM7 13h6v4H9v4H7v-8zm10 0v8h-2v-4h-4v-2h6z"/>
    </svg>
    <span class="fs-text">Fullscreen</span>
  </button>

  <iframe
    id="terminal-frame"
    src="https://www.interloper.ie/wp-content/uploads/2025/08/terminal2.html"
    title="terminal"
    loading="lazy"
    allow="fullscreen"
    allowfullscreen
  ></iframe>
</div>

<script>
(function(){
  const wrap   = document.getElementById('terminal-embed');
  const btn    = wrap.querySelector('.fs-btn');
  const label  = btn.querySelector('.fs-text');
  const svg    = btn.querySelector('svg');
  const iframe = document.getElementById('terminal-frame');

  function isFS(){
    return document.fullscreenElement === wrap
        || document.webkitFullscreenElement === wrap;
  }

  async function enterFS(){
    try{
      if (wrap.requestFullscreen) await wrap.requestFullscreen();
      else if (wrap.webkitRequestFullscreen) wrap.webkitRequestFullscreen(); // Safari
    }catch(e){ console.warn('Fullscreen request failed:', e); }
  }

  function exitFS(){
    if (document.exitFullscreen) document.exitFullscreen();
    else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
  }

  function toggleFS(){
    if (isFS()) exitFS();
    else enterFS();
  }

  // Try to focus the terminal inside the iframe
  function focusTerminalInIframe(){
    try {
      // direct same-origin call
      iframe.contentWindow?.focusTerminal?.();
    } catch(e) {
      // Cross-origin safety; shouldn't happen for same-site
      console.warn('Could not call focusTerminal on iframe:', e);
    }
  }

  function updateUI(){
    const active = isFS();
    wrap.classList.toggle('is-fullscreen', active);
    btn.setAttribute('aria-label', active ? 'Exit fullscreen' : 'Enter fullscreen');
    btn.title = active ? 'Exit fullscreen' : 'Fullscreen';
    label.textContent = active ? 'Exit' : 'Fullscreen';
    svg.style.transform = active ? 'rotate(180deg)' : 'none';

    // When entering fullscreen, nudge focus a few times to beat timing quirks
    if (active) {
      focusTerminalInIframe();
      setTimeout(focusTerminalInIframe, 50);
      setTimeout(focusTerminalInIframe, 150);
    }
  }

  btn.addEventListener('click', () => {
    toggleFS();
    // Also try immediately on click (helps on some browsers)
    setTimeout(focusTerminalInIframe, 0);
  });

  document.addEventListener('fullscreenchange', updateUI);
  document.addEventListener('webkitfullscreenchange', updateUI);
  updateUI();
})();
</script>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
