SuperAPI AI Directory

1c-web-session

Office & productivity

View repository

Updated 10-15 17:18

About this skill

/1c-web-session — Managing 1C in the web client

Skill for working with 1C:Enterprise via Playwright MCP (browser).

Source: adapted from RooLee10/1c-web-session (MIT)

Starting the database

  1. Read CLAUDE.md of the project — find the database URL (format: http://<сервер>/<публикация>/ru_RU/)
  2. browser_navigate to the URL
  3. Handle the license/login if they appear (see below)

Termination and restart

Service and settings → File → Exit → Terminate

Restart: terminate → browser_navigate to the URL.


CRITICAL RULES OF THE 1C web client

1. Clipboard paste — the ONLY input method

Standard fill(), type(), pressSequentially() do NOT work in 1C. Always:

await page.evaluate((v) => navigator.clipboard.writeText(v), 'Значение');
await page.keyboard.press('Control+A');  // очистить поле
await page.keyboard.press('Control+V');

2. Suffix selectors — the ONLY reliable method

1C forms receive incremental IDs (form26, form40). Never hardcode the form number.

// ПРАВИЛЬНО
await page.locator('[id$="_ВидНоменклатуры_DLB"]').click();
// НЕПРАВИЛЬНО — сломается после первой формы
await page.locator('[id="form40_ВидНоменклатуры_DLB"]').click();

3. DLB buttons — a universal selection pattern

All selection buttons have the suffix _DLB — both enumerations and catalogs:

await page.locator('[id$="_ВидАттракциона_DLB"]').click();
await page.waitForTimeout(100);

if (await page.locator('#editDropDown').count() > 0) {
  // Dropdown — перечисление или небольшой справочник
  await page.locator('#editDropDown').getByText('Экстремальный').click();
} else {
  // Picker — большой справочник
  await page.getByTitle('Выбрать значение (Ctrl+Enter)').waitFor();
  await page.getByText('Экстремальный').click();
  await page.getByTitle('Выбрать значение (Ctrl+Enter)').click();
}

4. Navigation cache

Section IDs (#themesCell_theme_N) and menu item IDs (#cmd_0_3_txt) are unique to each database.

On the first launch — execute references/scan-nav.js via browser_run_code, save the result to .claude/1c-nav.json (Write tool).

Navigation — only via cached IDs:

await page.locator('#themesCell_theme_1').click();  // из 1c-nav.json
await page.locator('#cmd_0_3_txt').waitFor({ state: 'visible', timeout: 2000 });
await page.locator('#cmd_0_3_txt').click();

Do NOT use getByText('Справочники') — strict mode violation.


Handling dialogs

License dialog

"No free license found!" — set the checkbox via JS, click "Run launch":

async (page) => {
  const rows = await page.locator('text=сеанс:').all();
  for (const row of rows) {
    const parent = row.locator('xpath=..');
    await parent.locator('input, [class*="check"], [class*="box"]').first().click();
  }
  return { clicked: rows.length };
}

Login form

If it appears — immediately browser_navigate with the same URL. Do not click "Log in".

Modal dialogs

Dialog Action
beforeunload browser_handle_dialog с accept: true
"Data has been modified" locator('a').filter({ hasText: 'Нет' }).click()
Confirmation (Mark for deletion?) page.keyboard.press('Enter')

Keyboard shortcuts

Shortcut Action
Ctrl+Enter Write and close / Post and close
Ctrl+S Write (without posting)
Escape Close error dialog
Tab Next field
F4 Show list for selection
Ins Add row to tabular section
Ctrl+Shift+Z Close validation errors panel
Delete Mark for deletion

Posting: Ctrl+Enter posts and generates movements. Ctrl+S only writes.


"Name" field on creation

The "Name" field is already focused when opening the new item form. Do not search by ID:

await page.keyboard.press('Control+A');
await page.evaluate((v) => navigator.clipboard.writeText(v), name);
await page.keyboard.press('Control+V');

Tabular sections

Add row (Ins), fill clipboard paste + Tab between fields.

Reference in cell:

await page.keyboard.press('F4');
await page.waitForTimeout(300);
await page.getByTitle('Выбрать значение (Ctrl+Enter)').waitFor();
const pickerSearch = page.getByRole('textbox', { name: 'Поиск (Ctrl+F)' }).last();
await pickerSearch.click();
await page.evaluate((v) => navigator.clipboard.writeText(v), 'Текст поиска');
await page.keyboard.press('Control+V');
await page.waitForTimeout(600);
await page.locator('.gridBoxText').filter({ hasText: /Текст поиска/ }).first().click({ force: true });
await page.getByTitle('Выбрать значение (Ctrl+Enter)').click();

With multiple tables on a form — scope to the desired command bar:

await page.locator('[id$="_ТоварыКоманднаяПанель"]').last()
  .getByTitle('Добавить новый элемент (Ins)').click();

Creating an object from a dropdown

If the required item is not in the reference:

await page.locator('[id$="_Филиал_DLB"]').click();
await page.waitForTimeout(200);
await page.getByTitle('Создать (F8)').click();
await page.waitForTimeout(500);
// Форма нового объекта — после Ctrl+Enter подставится автоматически

Groups in hierarchical references

Expand/collapse — double click. When creating inside an expanded group, the "Group" field is filled automatically.


JS verification (instead of screenshot)

After Ctrl+Enter check the result directly in browser_run_code:

await page.waitForTimeout(800);
const hasModal = await page.locator('.modalSurface').count() > 0;
const formOpen = await page.getByRole('textbox', { name: 'Наименование:' }).count() > 0;
const hasValidationPanel = await page.locator('[title="Закрыть (Ctrl+Shift+Z)"]')
  .filter({ visible: true }).count() > 0;

if (hasModal) return { status: 'error', reason: 'модальная ошибка' };
if (formOpen && hasValidationPanel) return { status: 'error', reason: 'ошибка валидации' };
if (formOpen) return { status: 'error', reason: 'форма не закрылась' };
return { status: 'ok' };

Running JS scripts

Command запусти сценарий <путь>.js — execute a file via browser_run_code.

Method A (default): clipboard loader

Get-Content -LiteralPath <путь> -Raw | Set-Clipboard

Then loader in browser_run_code:

async (page) => {
  let src;
  try { src = await page.evaluate(() => navigator.clipboard.readText()); }
  catch (e) { return { ok: false, stage: 'clipboard', message: String(e) }; }

  if (!src || !src.includes('async (page)'))
    return { ok: false, stage: 'clipboard', message: 'Нет сценария в буфере' };

  let scenario;
  try { scenario = (0, eval)(`(\n${src}\n)`); }
  catch (e) { return { ok: false, stage: 'compile', message: e.message }; }

  try { return { ok: true, result: await scenario(page) }; }
  catch (e) { return { ok: false, stage: 'runtime', message: e.message, stack: e.stack }; }
}

Method B (fallback): direct launch

If clipboard is unavailable — read the file, pass the code directly to browser_run_code.



Utility scripts

Ready-made scripts in .claude/skills/1c-web-session/scripts/ — include in browser_run_code via ${read(...)}.

1c-screenshot.js

Screenshots + selector cache (.claude/1c-web-cache.json):

  • screenshot1C(page, name, opts) — screenshot in playwright-screenshots/, auto-cleanup > 7 days
  • loadCache(baseUrl) / saveSelector(baseUrl, key, value) — read/write cache (dot notation)
  • locatorWithCache(page, baseUrl, cacheKey, fallbacks[]) — tries cache → fallbacks → saves the found one

1c-login.js (requires 1c-screenshot.js)

login1C(page, baseUrl, user, password) — login via clipboard paste, skips if already logged in.

1c-open-object.js

  • openObject1C(page, baseUrl, metadataPath, guid) — open object by GUID
  • openList1C(page, baseUrl, metadataPath) — open list

1c-snapshot.js (requires 1c-screenshot.js)

snapshot1C(page, navigateTo?) — collects visibleFields (label+value without screenshot) + form title.

1c-fill-form.js

fillForm1C(page, fields, options) — fills form fields via clipboard paste.

Example: open an object by GUID and read the fields

async (page) => {
  // paste content of 1c-open-object.js
  // paste content of 1c-screenshot.js
  // paste content of 1c-snapshot.js
  return await snapshot1C(page,
    'http://YOUR_EDT_SERVER/KAF/e1cib/data/Справочник.Номенклатура?ref=<guid>'
  );
}

Optimization

Detailed recommendations — in references/optimization.md. Key points:

  • One browser_run_code for the entire task — not step-by-step calls
  • Tab before the DLB click — removes focus from the input field
  • waitForTimeout(50) for dropdown — waitFor is redundant
  • JS verification inside — without screenshot
  • browser_snapshot only on the first type of the element or on error
  • If the chain fails → switch to step-by-step tools + browser_snapshot