← All Articles

Screen-to-HTML Modernization Guide

IMTerm's Screen-to-HTML feature transforms 24x80 (and 27x132) green-screen applications into styled HTML forms without any changes to the host application. The AS/400 or z/OS program continues to run exactly as it always has. Users see a modern web form instead of a terminal window. This guide explains how Screen-to-HTML works, how to configure it, and how to use the API for custom integrations.


What Screen-to-HTML Does

A typical AS/400 application screen looks like this in the terminal:

                    CUSTOMER MAINTENANCE
 Customer ID  . . . : ___________
 Name  . . . . . . . ___________________________
 Address  . . . . . . ___________________________
 City  . . . . . . . ___________

 F3=Exit  F5=Refresh  F6=Add  F12=Cancel

Screen-to-HTML analyzes the terminal screen and converts it to an HTML form that looks and behaves like a web application. Labels become <label> elements. Input fields become styled <input> elements. PF keys become <button> elements. The user interacts with the HTML form; IMTerm translates the interactions back to 5250 or 3270 data stream operations on the host connection.

The host program never knows a web form is involved. From the AS/400's perspective, it is talking to a standard 5250 terminal.


Two Delivery Modes

Screen-to-HTML can be used in two ways:

1. Modern View (interactive, in-browser) The terminal session switches from raw terminal display to the HTML form view. Users see the modern form and interact with it. All field interactions and PF key presses are relayed back to the AS/400 in real time.

To activate: View > Modern View or Ctrl+M from within a terminal session.

2. REST API (read-only or read-write, for integration) The IMTerm API exposes the current screen state as HTML or as a form schema. External systems can read current screen values, submit field values and AID keys, and drive the session programmatically.

GET /api/session/{id}/screen/html - returns the current screen as an HTML snippet. GET /api/session/{id}/screen/form - returns the screen's field model as JSON. POST /api/session/{id}/screen/submit - submits a form (field values + AID key).


How the Screen Analyzer Works

The Screen Analyzer (web/src/modernize/ScreenAnalyzer.ts) runs on each screen update and performs a multi-pass analysis:

Pass 1: Field extraction The 5250 or 3270 data stream includes explicit field descriptors: each input field has a position, length, field attribute (protected/unprotected, numeric, hidden), and current value. The analyzer extracts these directly from the protocol's field model.

Pass 2: Label association For each input field, the analyzer searches the surrounding non-field text to find the label. It looks for text to the left of the field on the same row first (the most common AS/400 layout), then above the field (for multi-line labels), then to the right. The text closest to the field edge is selected as the label.

Pass 3: Section grouping The analyzer identifies repeated row patterns (subfiles/grids) and groups them into HTML <table> elements. Non-repeating fields are grouped by proximity into fieldset sections.

Pass 4: PF key parsing The bottom two rows of most AS/400 screens contain the function key legend (F3=Exit, F5=Refresh, etc.). The analyzer parses these and converts each key definition to a <button> with the corresponding AID key.

Pass 5: Hebrew/BiDi detection If Hebrew characters are detected in field labels or values, the analyzer sets dir="rtl" on affected elements and reverses the label/field ordering for Hebrew layout conventions.


Enabling Modern View

Open a terminal session that is connected and showing a screen. Press Ctrl+M or go to View > Modern View.

IMTerm switches the display from the xterm.js terminal renderer to the HTML form renderer. The session remains connected to the AS/400 - you are seeing the same screen data, just rendered differently.

To switch back to terminal view: press Ctrl+M again or View > Terminal View.

Modern View preference is saved per session. If you connect to the same host with the same profile, the view preference is restored.


Configuring the Modernization Engine

In your config.yaml, the modernize section controls screen analysis behavior:

modernize:
  # Enable Modern View toggle for users (default: true)
  enabled: true

  # Use custom HTML template for form rendering.
  # The template receives the screen model as a JSON variable.
  template: ""   # path to custom .html template file, or empty for built-in

  # Brand color for form chrome (buttons, header).
  # CSS color string. Default inherits from the site theme.
  brand_color: "#2563eb"

  # Label detection sensitivity.
  # higher = more labels detected; lower = only clear adjacencies.
  label_sensitivity: 0.7

  # Maximum subfile rows before switching to paginated table.
  subfile_page_size: 20

  # CSS classes to inject into the generated HTML.
  # Useful for integrating with your own design system.
  css_classes:
    form: "im-form"
    fieldset: "im-fieldset"
    input: "im-input"
    label: "im-label"
    button: "im-btn"
    table: "im-table"

Custom HTML Templates

The built-in HTML renderer uses a clean, unstyled form layout. For organizations that want to match a specific brand or embed the form in an existing portal, custom templates are supported.

Create a template file at templates/modernize.html:

<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="/your-brand-styles.css">
</head>
<body>
  <div class="portal-wrapper">
    <h2>{{ .Screen.Title }}</h2>
    <form id="im-form" method="POST">
      {{ range .Screen.Sections }}
      <fieldset class="portal-section">
        <legend>{{ .Title }}</legend>
        {{ range .Fields }}
        <div class="portal-row">
          <label for="{{ .ID }}">{{ .Label }}</label>
          {{ if .Protected }}
            <span class="portal-value">{{ .Value }}</span>
          {{ else }}
            <input id="{{ .ID }}" name="{{ .Name }}"
                   value="{{ .Value }}"
                   maxlength="{{ .Length }}"
                   {{ if .Numeric }}inputmode="numeric"{{ end }}
                   {{ if .Hidden }}type="password"{{ end }}>
          {{ end }}
        </div>
        {{ end }}
      </fieldset>
      {{ end }}
      <div class="portal-actions">
        {{ range .Screen.Keys }}
        <button type="submit" name="_aid" value="{{ .AID }}">{{ .Label }}</button>
        {{ end }}
      </div>
    </form>
  </div>
</body>
</html>

Set the template path in config.yaml:

modernize:
  template: "/etc/imterm/templates/modernize.html"

REST API Reference

GET /api/session/{id}/screen/html

Returns the current screen as an HTML snippet (not a full document). Suitable for embedding in an iframe or injecting into a portal page.

curl -H "Authorization: Bearer $TOKEN" \
     https://imterm.example.com/api/session/sess_abc123/screen/html

Response is text/html. The snippet contains the form element and all fields.

GET /api/session/{id}/screen/form

Returns the screen model as a JSON object. Useful for custom renderers or automation scripts.

{
  "session_id": "sess_abc123",
  "screen_size": {"rows": 24, "cols": 80},
  "title": "CUSTOMER MAINTENANCE",
  "cursor": {"row": 3, "col": 20},
  "fields": [
    {
      "id": "fld_0320",
      "name": "CUSTID",
      "label": "Customer ID",
      "row": 3, "col": 20,
      "length": 10,
      "value": "",
      "protected": false,
      "numeric": false,
      "hidden": false,
      "rtl": false
    }
  ],
  "sections": [
    {
      "title": "",
      "field_ids": ["fld_0320", "fld_0420", "fld_0520"]
    }
  ],
  "keys": [
    {"aid": "F3",  "label": "Exit"},
    {"aid": "F5",  "label": "Refresh"},
    {"aid": "F6",  "label": "Add"},
    {"aid": "F12", "label": "Cancel"}
  ]
}

POST /api/session/{id}/screen/submit

Submit field values and an AID key, as if the user filled in the form and clicked a button.

curl -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "fields": {
         "CUSTID": "0001234",
         "NAME": "Moshe Cohen"
       },
       "aid": "F6"
     }' \
     https://imterm.example.com/api/session/sess_abc123/screen/submit

The response is the next screen state (same format as GET /screen/form), allowing you to chain multiple screen interactions in a single workflow.


Automating Multi-Screen Workflows

The submit API enables automation of multi-screen AS/400 workflows. For example, to automate a customer lookup:

// Navigate to customer maintenance
await fetch(`/api/session/${sessionId}/screen/submit`, {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ fields: {}, aid: 'ENTER' })
})

// Read the resulting screen
const screen = await fetch(`/api/session/${sessionId}/screen/form`, {
  headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json())

// Find the CUSTID field and submit a customer number
const fields = {}
fields['CUSTID'] = '0001234'
await fetch(`/api/session/${sessionId}/screen/submit`, {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ fields, aid: 'ENTER' })
})

// Read the customer detail screen
const detail = await fetch(`/api/session/${sessionId}/screen/form`, {
  headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json())

console.log('Customer name:', detail.fields.find(f => f.name === 'NAME')?.value)

This is the foundation of IMTerm's Agent Mode, which uses the Screen-to-HTML API to let AI models drive AS/400 sessions using natural language instructions.


Embedding in a Web Portal

To embed a live AS/400 form in your web portal without showing the raw terminal, use the HTML endpoint in an iframe:

<!-- In your portal page -->
<iframe
  id="as400-frame"
  src="https://imterm.example.com/api/session/SESS_ID/screen/html?embed=1"
  width="800"
  height="600"
  style="border: 1px solid #ddd; border-radius: 4px;"
></iframe>

The embed=1 parameter tells IMTerm to return a full HTML document (with <html>, <head>, and <body> tags) suitable for direct iframe embedding. Form submissions inside the iframe update the AS/400 screen and refresh the iframe content automatically via the JavaScript bridge.


Limitations

Protected fields with format characters: Some AS/400 screens use protected fields filled with asterisks or dashes as visual separators. The analyzer recognizes common patterns (all-asterisk, all-dash lines) and omits them from the rendered form. Unusual separator patterns may appear as labeled read-only fields.

Subfiles with many rows: Subfiles (AS/400 grids/lists) are rendered as HTML tables. Very large subfiles (hundreds of rows) use client-side pagination controlled by the subfile_page_size setting.

Popup windows: Some AS/400 applications use windows (overlaid screens). IMTerm detects the window boundaries and renders the popup content as a modal dialog. Window content not recognized as a popup defaults to full-screen form rendering.

Custom display attributes: AS/400 programs can set color, highlighting, and blink attributes on individual characters. The Screen-to-HTML renderer preserves color as CSS color and converts highlighting to font-weight: bold. Blink attributes are converted to a CSS animation but can be disabled per user preference.


Hebrew Screen Modernization

For Hebrew AS/400 applications, Screen-to-HTML handles right-to-left layout automatically:

  • The form root gets dir="rtl" when Hebrew is detected.
  • Labels appear to the right of their input fields (Hebrew convention).
  • Subfile column headers are right-aligned.
  • PF key buttons are ordered right-to-left.
  • Input fields get dir="rtl" to ensure right-to-left cursor movement.

No configuration is needed - the BiDi detection runs automatically on every screen update.