इस पेज का अनुवाद मशीन द्वारा किया गया है और इसमें त्रुटियाँ हो सकती हैं। मूल अंग्रेज़ी पढ़ें

एलिमेंट

WebView

वेब व्यू ऐप के अंदर कोई वेब पेज या आपके द्वारा दिया गया HTML दिखाता है: iOS और Mac Catalyst पर WKWebView, और Android पर android.webkit.WebView। क्योंकि NSH नेटिव व्यू के साथ रेंडर करता है, इसलिए वेब व्यू लेआउट में किसी भी अन्य व्यू की तरह अपने आसपास के व्यू के क्रम में रहता है। Rust में इसे web_view(url) या web_view_html(html, base_url) से बनाया जाता है, और TSX में nsh से <WebView> द्वारा।

el-web-view.tsx
el-web-view.tsxLive
import { mount, signal, Text, View, WebView, type WebViewNavigation, type WebViewRef } from "nsh";
import { platform, windowSize } from "nsh";
import { button, buttonText, ui } from "./palette";

// The page: a button that posts to the app, and a line that shows what the
// app posts back.
const page = (c: ReturnType<typeof ui>) => `<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Inline page</title>
<style>
  body { font: 15px system-ui, sans-serif; margin: 0; padding: 16px; background: ${c.page}; color: ${c.text}; }
  button { font: inherit; font-weight: 600; padding: 10px 14px; border-radius: 10px; border: 1px solid ${c.border}; background: ${c.accent}; color: ${c.page}; }
  .muted { color: ${c.muted}; }
  #got { margin-top: 12px; }
  #box { margin-top: 12px; height: 48px; border-radius: 10px; background: ${c.border}; transition: background 0.3s; }
</style>
<p class="muted">This is HTML in a web view.</p>
<button id="post">postMessage to the app</button>
<p id="got" class="muted">Nothing from the app yet.</p>
<div id="box"></div>
<script>
  let taps = 0;
  document.getElementById("post").onclick = () =>
    window.nsh.postMessage("tap " + ++taps + " from the page");
  window.addEventListener("message", (e) => { document.getElementById("got").textContent = "App says: " + e.data; });
</script>`;

function Button(props: { label: string; onPress: () => void }) {
  return (
    <View
      onPress={props.onPress}
      {...button()}
    >
      <Text style={buttonText()}>{props.label}</Text>
    </View>
  );
}

const colors = () => [ui().accent, ui().info, ui().violet, ui().down, ui().warn];

function App() {
  const messages = signal<string[]>([]);
  const nav = signal<WebViewNavigation | null>(null);
  const sent = signal(0);
  let painted = 0;
  let web!: WebViewRef;
  const wide = () => windowSize().width >= 700;

  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>web-view</Text>
      <View style={{ flex: 1, flexDirection: wide() ? "row" : "column", gap: 16 }}>
        <WebView
          ref={web}
          html={page(ui())}
          // Runs after each load: the page reports its title.
          injectedJavaScript={`window.nsh.postMessage("loaded: " + document.title)`}
          onLoad={(e) => nav.set(e)}
          onMessage={(e) => messages.set([e.data, ...messages()].slice(0, 4))}
          style={{ flex: 1, minHeight: 240, borderRadius: 14, overflow: "hidden", borderWidth: 1, borderColor: ui().border }}
        />
        <View style={{ gap: 12, width: wide() ? 320 : undefined }}>
          <Text style={{ color: ui().muted }}>
            {platform.desktop ? "Click" : "Tap"} the button in the page to post to the app; these buttons call the ref's methods.
          </Text>
          <View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
            <Button
              label="postMessage"
              onPress={() => {
                sent.update((n) => n + 1);
                web.postMessage(`hello #${sent()}`);
              }}
            />
            <Button
              label="injectJavaScript"
              onPress={() => {
                const color = colors()[painted++ % colors().length];
                web.injectJavaScript(`document.getElementById("box").style.background = "${color}"`);
              }}
            />
            <Button label="reload" onPress={() => web.reload()} />
          </View>
          <View style={{ ...ui().card, borderRadius: 12, padding: 12, gap: 4 }}>
            <Text style={{ color: ui().muted, fontSize: 12 }}>onLoad</Text>
            <Text style={{ numberOfLines: 1, color: ui().text, fontFamily: "monospace", fontSize: 13 }}>
              {nav() ? `title "${nav()!.title}"` : "…"}
            </Text>
            <Text style={{ color: ui().muted, fontSize: 12, marginTop: 6 }}>onMessage, newest first</Text>
            <Each of={messages()}>
              {(m) => <Text style={{ color: ui().text, fontFamily: "monospace", fontSize: 13 }}>{m()}</Text>}
            </Each>
          </View>
        </View>
      </View>
    </View>
  );
}

mount(App);
use nsh_core::prelude::*;

fn browser() -> impl IntoElement {
    let title = signal(SharedString::from("Loading…"));
    view()
        .flex_1()
        .child(text(title).number_of_lines(1))
        .child(
            web_view("https://example.com")
                .flex_1()
                .on_navigation_state_change(move |e: &WebNavigation| title.set(e.title.clone().into())),
        )
}

TSX में वेब व्यू src और html में से एक लेता है (देखें WebView API)। वेब व्यू और वीडियो गाइड में मैसेजिंग पैटर्न और कैपेबिलिटी की जानकारी है।

#Props

वेब व्यू एक व्यू के props लेता है (इसमें कोई चाइल्ड नहीं होता) और साथ में ये भी:

PropRustTypeDefaultविवरण
srcweb_view(url)stringदिखाए जाने वाले पेज का URL।
htmlweb_view_html(html, base_url), .html(html, base_url)stringइनलाइन HTML, जो src की जगह दिखाया जाता है।
baseUrlweb_view_html / .html का base_urlstringवह आधार जिसके विरुद्ध html के रिलेटिव लिंक हल किए जाते हैं।
injectedJavaScript.injected_java_script(..)stringहर पेज लोड होने के बाद चलता है।
injectedJavaScriptBeforeContentLoaded.injected_java_script_before_content(..)stringहर पेज की अपनी स्क्रिप्ट से पहले चलता है।
userAgent.user_agent(..)stringब्राउज़र की user agent स्ट्रिंग।
javaScriptEnabled.java_script_enabled(..)booleantrueपेज स्क्रिप्ट चलाते हैं।
scrollable.scrollable(..)booleantrueउपयोगकर्ता पेज को स्क्रॉल कर सकता है।
ios keyRustTypeविवरण
allowsBackForwardNavigationGestures.back_forward_gestures(..)booleanपीछे और आगे जाने के लिए किनारों से स्वाइप करें।
desktop keyRustTypeविवरण
allowsBackForwardNavigationGestures.back_forward_gestures(..)booleanmacOS: पीछे और आगे जाने के लिए ट्रैकपैड पर दो उंगलियों से स्वाइप करें; Windows: टचस्क्रीन पर स्वाइप करें; Linux: टचपैड पर स्वाइप करें।

#Events

EventRustPayloadकब
onLoadStart.on_load_startWebViewNavigationकोई पेज लोड होना शुरू हुआ।
onLoad.on_loadWebViewNavigationकोई पेज लोड होना पूरा हुआ।
onError.on_error{ message, code? } (&str, संदेश)लोडिंग विफल रही; किसी पेज के लिए code का मान "ERR_ACCESS_DENIED" होता है, या html जिसकी अनुमति ऐप की कैपेबिलिटी नहीं देतीं।
onNavigationStateChange.on_navigation_state_changeWebViewNavigationURL, शीर्षक, लोडिंग की स्थिति या हिस्ट्री बदली।
onMessage.on_message{ data: string } (&str)पेज ने window.nsh.postMessage(data) को कॉल किया।
onDownloadWebViewDownloadकोई डाउनलोड शुरू हुआ, पूरा हुआ या विफल रहा (macOS)।

WebViewNavigation का मान { url, title, loading, canGoBack, canGoForward } है; Rust में WebNavigation के साथ url, title, loading, can_go_back और can_go_forward होते हैं।

WebViewDownload का मान { state, id, url, filename, path, bytes?, message? } है: state का मान "started", "finished" (फ़ाइल के bytes के साथ) या "failed" (इसके message के साथ) होता है। onDownload लिसनर होने पर, पेज जो कुछ डाउनलोड करता है (अटैचमेंट, ऐसा टाइप जिसे वेब व्यू नहीं दिखाता, या download लिंक) वह ऐप की डेटा डायरेक्टरी में downloads/<id>/<filename> के रूप में सहेजा जाता है, जहाँ path इंगित करता है, और ऐप उसे fs/promises से पढ़ता है।

macOS पर, लिसनर जोड़ने से webview कैपेबिलिटी द्वारा अनुमत कोई भी लोड किया गया पेज डाउनलोड अपने-आप सहेज सकता है। स्टोरेज बजट की ज़िम्मेदारी ऐप की है, और पूरी हो चुकी फ़ाइलों का उपयोग समाप्त होने पर उन्हें downloads/ से हटाने की भी। वेब व्यू हटाने पर चल रहे डाउनलोड रद्द हो जाते हैं और उनकी अधूरी डायरेक्टरी हटा दी जाती हैं। डाउनलोड onDownload के माध्यम से रिपोर्ट होते हैं; किसी नेविगेशन को डाउनलोड में बदलने पर onError केवल वास्तविक नेविगेशन विफलताओं के लिए रखा जाता है। फ़ाइल नामों से कंट्रोल कैरेक्टर और द्विदिशीय फ़ॉर्मैटिंग कंट्रोल हटा दिए जाते हैं, और लंबे नाम 200 बाइट तक छोटे कर दिए जाते हैं, साथ ही जो एक्सटेंशन समा सके उसे सुरक्षित रखा जाता है।

#Ref methods

MethodRustविवरण
reload()reload()पेज को रीलोड करता है।
goBack() / goForward()go_back() / go_forward()पेज की हिस्ट्री में आगे-पीछे जाता है।
stopLoading()stop_loading()लोडिंग रोकता है।
injectJavaScript(script)inject_java_script(script)पेज में JavaScript चलाता है।
postMessage(data)post_message(data)पेज को एक स्ट्रिंग भेजता है, जिसे window पर message इवेंट के रूप में प्राप्त होती है।

#Messaging

ऐप और पेज स्ट्रिंग का आदान-प्रदान करते हैं। पेज window.nsh.postMessage(data) से पोस्ट करता है; ऐप ref के postMessage(data) (Rust में post_message(..)) से पोस्ट करता है। संरचित डेटा के लिए JSON भेजें।

use nsh_core::prelude::*;

const PAGE: &str = r#"<!doctype html>
<meta name="viewport" content="width=device-width">
<button onclick="window.nsh.postMessage('hello from the page')">Post to the app</button>
<p id="got"></p>
<script>
  window.addEventListener("message", (e) => { document.getElementById("got").textContent = e.data; });
</script>"#;

fn bridge() -> impl IntoElement {
    let message = signal(String::new());
    let web = element_ref();
    view()
        .gap_3()
        .child(
            web_view_html(PAGE.to_string(), None)
                .element_ref(&web)
                .h(px(200.0))
                .on_message(move |data: &str| message.set(data.to_string())),
        )
        .child(
            text(move || format!("Post to the page · got: {}", message.get()))
                .on_press(move || web.post_message("hi from the app")),
        )
}

#Capabilities

जब ऐप nsh.json में कैपेबिलिटी घोषित करता है, तो वेब व्यू webview के अंतर्गत सूचीबद्ध होस्ट ही दिखाता है और उन्हीं पर नेविगेट करता है। किसी अन्य होस्ट पर src, या webview के बिना ऐप में html, खाली व्यू दिखाता है; किसी अन्य होस्ट पर टॉप-लेवल नेविगेशन (उपयोगकर्ता द्वारा टैप किया गया लिंक) नेटिव स्तर पर रद्द कर दिया जाता है। हर स्थिति code "ERR_ACCESS_DENIED" के साथ onError रिपोर्ट करती है।

{
  "capabilities": {
    "webview": { "hosts": ["example.com", "*.example.com"] }
  }
}

#Platform notes

  • iOS पहली बार वेब व्यू बनने पर WebKit लोड करता है, इसलिए ऐप को इसकी कीमत तभी चुकानी पड़ती है जब वह वेब व्यू दिखाए।
  • macOS पर onDownload डाउनलोड को WebKit के WKDownload से सहेजता है।
  • Android पर पेज की postMessage कॉल बैकग्राउंड थ्रेड पर आती हैं और क्रम से ऐप को फ़ॉरवर्ड की जाती हैं।
  • वेब व्यू के कोने गोल करने के लिए उसे कॉर्नर रेडियस और overflow: "hidden" (.overflow_hidden()) दें।