@ait-co/devtools 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,7 +71,9 @@ const DEFAULT_STATE = {
71
71
  },
72
72
  ads: {
73
73
  isLoaded: false,
74
- nextEvent: "loaded"
74
+ nextEvent: "loaded",
75
+ forceNoFill: false,
76
+ lastEvent: null
75
77
  },
76
78
  game: {
77
79
  profile: {
@@ -1335,6 +1337,192 @@ function renderDeviceTab() {
1335
1337
  return container;
1336
1338
  }
1337
1339
  //#endregion
1340
+ //#region src/mock/ads/index.ts
1341
+ /**
1342
+ * 광고 mock (GoogleAdMob, TossAds, FullScreenAd)
1343
+ */
1344
+ function withIsSupported(fn) {
1345
+ fn.isSupported = () => true;
1346
+ return fn;
1347
+ }
1348
+ const GoogleAdMob = createMockProxy("GoogleAdMob", {
1349
+ loadAppsInTossAdMob: withIsSupported((args) => {
1350
+ setTimeout(() => {
1351
+ if (aitState.state.ads.forceNoFill) {
1352
+ args.onError(/* @__PURE__ */ new Error("No fill"));
1353
+ return;
1354
+ }
1355
+ aitState.patch("ads", { isLoaded: true });
1356
+ args.onEvent({
1357
+ type: "loaded",
1358
+ data: { adGroupId: args.options?.adGroupId }
1359
+ });
1360
+ }, 200);
1361
+ return () => {};
1362
+ }),
1363
+ showAppsInTossAdMob: withIsSupported((args) => {
1364
+ if (!aitState.state.ads.isLoaded) {
1365
+ args.onError(/* @__PURE__ */ new Error("Ad not loaded"));
1366
+ return () => {};
1367
+ }
1368
+ setTimeout(() => args.onEvent({ type: "requested" }), 50);
1369
+ setTimeout(() => args.onEvent({ type: "show" }), 100);
1370
+ setTimeout(() => args.onEvent({ type: "impression" }), 150);
1371
+ setTimeout(() => {
1372
+ args.onEvent({
1373
+ type: "userEarnedReward",
1374
+ data: {
1375
+ unitType: "coins",
1376
+ unitAmount: 10
1377
+ }
1378
+ });
1379
+ }, 1e3);
1380
+ setTimeout(() => {
1381
+ args.onEvent({ type: "dismissed" });
1382
+ aitState.patch("ads", { isLoaded: false });
1383
+ }, 1500);
1384
+ return () => {};
1385
+ }),
1386
+ isAppsInTossAdMobLoaded: withIsSupported(async (_options) => aitState.state.ads.isLoaded)
1387
+ });
1388
+ createMockProxy("TossAds", {
1389
+ initialize: withIsSupported((_options) => {
1390
+ console.log("[@ait-co/devtools] TossAds.initialize (mock)");
1391
+ }),
1392
+ attach: withIsSupported((_adGroupId, target, _options) => {
1393
+ const el = typeof target === "string" ? document.querySelector(target) : target;
1394
+ if (el) {
1395
+ const placeholder = document.createElement("div");
1396
+ placeholder.style.cssText = "background:#f0f0f0;border:1px dashed #999;padding:16px;text-align:center;color:#666;font-size:14px;";
1397
+ placeholder.textContent = "[@ait-co/devtools] TossAds Placeholder";
1398
+ el.appendChild(placeholder);
1399
+ }
1400
+ }),
1401
+ attachBanner: withIsSupported((_adGroupId, target, _options) => {
1402
+ const el = typeof target === "string" ? document.querySelector(target) : target;
1403
+ if (el) {
1404
+ const placeholder = document.createElement("div");
1405
+ placeholder.style.cssText = "background:#f0f0f0;border:1px dashed #999;padding:12px;text-align:center;color:#666;font-size:12px;";
1406
+ placeholder.textContent = "[@ait-co/devtools] Banner Ad Placeholder";
1407
+ el.appendChild(placeholder);
1408
+ }
1409
+ return { destroy: () => {} };
1410
+ }),
1411
+ destroy: withIsSupported((_slotId) => {}),
1412
+ destroyAll: withIsSupported(() => {})
1413
+ });
1414
+ const loadFullScreenAd = withIsSupported((args) => {
1415
+ setTimeout(() => {
1416
+ if (aitState.state.ads.forceNoFill) {
1417
+ args.onError(/* @__PURE__ */ new Error("No fill"));
1418
+ return;
1419
+ }
1420
+ aitState.patch("ads", { isLoaded: true });
1421
+ args.onEvent({
1422
+ type: "loaded",
1423
+ data: { adGroupId: args.options?.adGroupId }
1424
+ });
1425
+ }, 200);
1426
+ return () => {};
1427
+ });
1428
+ const showFullScreenAd = withIsSupported((args) => {
1429
+ if (!aitState.state.ads.isLoaded) {
1430
+ args.onError(/* @__PURE__ */ new Error("Ad not loaded"));
1431
+ return () => {};
1432
+ }
1433
+ setTimeout(() => args.onEvent({ type: "show" }), 100);
1434
+ setTimeout(() => args.onEvent({ type: "dismissed" }), 1500);
1435
+ return () => {};
1436
+ });
1437
+ //#endregion
1438
+ //#region src/panel/tabs/ads.ts
1439
+ function recordEvent(type) {
1440
+ aitState.patch("ads", { lastEvent: {
1441
+ type,
1442
+ timestamp: Date.now()
1443
+ } });
1444
+ }
1445
+ function recordError(message) {
1446
+ recordEvent(`error: ${message}`);
1447
+ }
1448
+ function statusRow(label, value) {
1449
+ return h("div", { className: "ait-row" }, h("label", {}, label), h("span", { style: "font-family:SF Mono,Menlo,monospace;font-size:11px;color:#aaa" }, value));
1450
+ }
1451
+ function lastEventLine() {
1452
+ const last = aitState.state.ads.lastEvent;
1453
+ if (!last) return h("div", { className: "ait-log-entry" }, h("span", { style: "color:#555" }, "No events yet"));
1454
+ const time = new Date(last.timestamp).toLocaleTimeString();
1455
+ return h("div", { className: "ait-log-entry" }, h("span", {
1456
+ className: "ait-log-type",
1457
+ style: last.type.startsWith("error:") ? "color:#e74c3c" : ""
1458
+ }, last.type), h("span", { className: "ait-log-time" }, time));
1459
+ }
1460
+ function adSection(title, onLoad, onShow, disabled) {
1461
+ const loadBtn = h("button", { className: "ait-btn ait-btn-sm" }, "Load");
1462
+ const showBtn = h("button", { className: "ait-btn ait-btn-sm" }, "Show");
1463
+ if (disabled) {
1464
+ loadBtn.disabled = true;
1465
+ showBtn.disabled = true;
1466
+ }
1467
+ loadBtn.addEventListener("click", onLoad);
1468
+ showBtn.addEventListener("click", onShow);
1469
+ return h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, title), h("div", { className: "ait-btn-row" }, loadBtn, showBtn));
1470
+ }
1471
+ function renderAdsTab() {
1472
+ const s = aitState.state;
1473
+ const disabled = !s.panelEditable;
1474
+ const container = h("div");
1475
+ if (disabled) container.appendChild(monitoringNotice());
1476
+ const forceNoFillCb = h("input", {
1477
+ type: "checkbox",
1478
+ className: "ait-checkbox"
1479
+ });
1480
+ forceNoFillCb.checked = s.ads.forceNoFill;
1481
+ if (disabled) forceNoFillCb.disabled = true;
1482
+ forceNoFillCb.addEventListener("change", () => {
1483
+ aitState.patch("ads", { forceNoFill: forceNoFillCb.checked });
1484
+ });
1485
+ container.append(h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, "Ads State"), statusRow("isLoaded", String(s.ads.isLoaded)), h("div", { className: "ait-row" }, h("label", {}, "Force \"no fill\""), forceNoFillCb), lastEventLine()), adSection("GoogleAdMob", () => {
1486
+ GoogleAdMob.loadAppsInTossAdMob({
1487
+ onEvent: (e) => recordEvent(e.type),
1488
+ onError: (err) => recordError(err.message)
1489
+ });
1490
+ }, () => {
1491
+ GoogleAdMob.showAppsInTossAdMob({
1492
+ onEvent: (e) => recordEvent(e.type),
1493
+ onError: (err) => recordError(err.message)
1494
+ });
1495
+ }, disabled), adSection("TossAds", () => {
1496
+ if (aitState.state.ads.forceNoFill) {
1497
+ recordError("No fill");
1498
+ return;
1499
+ }
1500
+ aitState.patch("ads", { isLoaded: true });
1501
+ recordEvent("loaded");
1502
+ }, () => {
1503
+ if (!aitState.state.ads.isLoaded) {
1504
+ recordError("Ad not loaded");
1505
+ return;
1506
+ }
1507
+ recordEvent("show");
1508
+ setTimeout(() => {
1509
+ recordEvent("dismissed");
1510
+ aitState.patch("ads", { isLoaded: false });
1511
+ }, 1500);
1512
+ }, disabled), adSection("FullScreenAd", () => {
1513
+ loadFullScreenAd({
1514
+ onEvent: (e) => recordEvent(e.type),
1515
+ onError: (err) => recordError(err.message)
1516
+ });
1517
+ }, () => {
1518
+ showFullScreenAd({
1519
+ onEvent: (e) => recordEvent(e.type),
1520
+ onError: (err) => recordError(err.message)
1521
+ });
1522
+ }, disabled));
1523
+ return container;
1524
+ }
1525
+ //#endregion
1338
1526
  //#region src/panel/tabs/analytics.ts
1339
1527
  function renderAnalyticsTab() {
1340
1528
  const disabled = !aitState.state.panelEditable;
@@ -2281,6 +2469,10 @@ const TABS = [
2281
2469
  id: "iap",
2282
2470
  label: "IAP"
2283
2471
  },
2472
+ {
2473
+ id: "ads",
2474
+ label: "Ads"
2475
+ },
2284
2476
  {
2285
2477
  id: "events",
2286
2478
  label: "Events"
@@ -2302,6 +2494,7 @@ function createTabRenderers(refreshPanel) {
2302
2494
  device: renderDeviceTab,
2303
2495
  viewport: renderViewportTab,
2304
2496
  iap: renderIapTab,
2497
+ ads: renderAdsTab,
2305
2498
  events: renderEventsTab,
2306
2499
  analytics: renderAnalyticsTab,
2307
2500
  storage: () => renderStorageTab(refreshPanel)
@@ -2502,7 +2695,7 @@ function mount() {
2502
2695
  mockBadge.textContent = aitState.state.panelEditable ? "EDIT" : "READ-ONLY";
2503
2696
  refreshPanel();
2504
2697
  });
2505
- const headerRight = h("span", { style: "display:flex;align-items:center;gap:6px" }, mockBadge, h("span", { style: "font-size:11px;color:#666;font-weight:400" }, `v0.1.7`), closeBtn);
2698
+ const headerRight = h("span", { style: "display:flex;align-items:center;gap:6px" }, mockBadge, h("span", { style: "font-size:11px;color:#666;font-weight:400" }, `v0.1.8`), closeBtn);
2506
2699
  const header = h("div", { className: "ait-panel-header" }, h("span", {}, "AIT DevTools"), headerRight);
2507
2700
  tabsEl = h("div", { className: "ait-panel-tabs" });
2508
2701
  for (const tab of TABS) {
@@ -2541,7 +2734,7 @@ function mount() {
2541
2734
  });
2542
2735
  aitState.subscribe(() => {
2543
2736
  try {
2544
- if (isOpen && (currentTab === "analytics" || currentTab === "storage" || currentTab === "device" || currentTab === "viewport" || currentTab === "iap")) refreshPanel();
2737
+ if (isOpen && (currentTab === "analytics" || currentTab === "storage" || currentTab === "device" || currentTab === "viewport" || currentTab === "iap" || currentTab === "ads")) refreshPanel();
2545
2738
  } catch (err) {
2546
2739
  console.error("[@ait-co/devtools] Error in subscribe callback:", err);
2547
2740
  }